From f7f0957188d48ec5278107dc8fbd2de747fda1b8 Mon Sep 17 00:00:00 2001 From: Yuxin Zhou Date: Sat, 29 Jan 2022 00:24:03 +0000 Subject: [PATCH] Release 6.1.10 --- common/inc/tx_api.h | 12 +- common_modules/inc/txm_module.h | 42 +- common_modules/inc/txm_module_user_sample.h | 159 +- .../module_lib/src/txm_block_allocate.c | 74 +- .../module_lib/src/txm_block_pool_create.c | 82 +- .../module_lib/src/txm_block_pool_delete.c | 68 +- .../module_lib/src/txm_block_pool_info_get.c | 82 +- .../src/txm_block_pool_performance_info_get.c | 80 +- ...m_block_pool_performance_system_info_get.c | 76 +- .../src/txm_block_pool_prioritize.c | 62 +- .../module_lib/src/txm_block_release.c | 64 +- .../module_lib/src/txm_byte_allocate.c | 78 +- .../module_lib/src/txm_byte_pool_create.c | 80 +- .../module_lib/src/txm_byte_pool_delete.c | 68 +- .../module_lib/src/txm_byte_pool_info_get.c | 82 +- .../src/txm_byte_pool_performance_info_get.c | 94 +- ...xm_byte_pool_performance_system_info_get.c | 90 +- .../module_lib/src/txm_byte_pool_prioritize.c | 62 +- .../module_lib/src/txm_byte_release.c | 66 +- .../module_lib/src/txm_event_flags_create.c | 72 +- .../module_lib/src/txm_event_flags_delete.c | 68 +- .../module_lib/src/txm_event_flags_get.c | 84 +- .../module_lib/src/txm_event_flags_info_get.c | 84 +- .../txm_event_flags_performance_info_get.c | 82 +- ..._event_flags_performance_system_info_get.c | 76 +- .../module_lib/src/txm_event_flags_set.c | 74 +- .../src/txm_event_flags_set_notify.c | 68 +- .../src/txm_module_application_request.c | 95 +- ...txm_module_callback_request_thread_entry.c | 138 +- .../src/txm_module_object_allocate.c | 26 +- .../src/txm_module_object_deallocate.c | 14 +- .../src/txm_module_object_pointer_get.c | 10 +- .../txm_module_object_pointer_get_extended.c | 10 +- .../src/txm_module_thread_system_suspend.c | 76 +- .../module_lib/src/txm_mutex_create.c | 74 +- .../module_lib/src/txm_mutex_delete.c | 68 +- common_modules/module_lib/src/txm_mutex_get.c | 68 +- .../module_lib/src/txm_mutex_info_get.c | 84 +- .../src/txm_mutex_performance_info_get.c | 86 +- .../txm_mutex_performance_system_info_get.c | 82 +- .../module_lib/src/txm_mutex_prioritize.c | 62 +- common_modules/module_lib/src/txm_mutex_put.c | 64 +- .../module_lib/src/txm_queue_create.c | 76 +- .../module_lib/src/txm_queue_delete.c | 66 +- .../module_lib/src/txm_queue_flush.c | 66 +- .../module_lib/src/txm_queue_front_send.c | 72 +- .../module_lib/src/txm_queue_info_get.c | 82 +- .../src/txm_queue_performance_info_get.c | 84 +- .../txm_queue_performance_system_info_get.c | 84 +- .../module_lib/src/txm_queue_prioritize.c | 62 +- .../module_lib/src/txm_queue_receive.c | 76 +- .../module_lib/src/txm_queue_send.c | 72 +- .../module_lib/src/txm_queue_send_notify.c | 68 +- .../src/txm_semaphore_ceiling_put.c | 70 +- .../module_lib/src/txm_semaphore_create.c | 72 +- .../module_lib/src/txm_semaphore_delete.c | 68 +- .../module_lib/src/txm_semaphore_get.c | 68 +- .../module_lib/src/txm_semaphore_info_get.c | 82 +- .../src/txm_semaphore_performance_info_get.c | 78 +- ...xm_semaphore_performance_system_info_get.c | 74 +- .../module_lib/src/txm_semaphore_prioritize.c | 62 +- .../module_lib/src/txm_semaphore_put.c | 64 +- .../module_lib/src/txm_semaphore_put_notify.c | 68 +- .../module_lib/src/txm_thread_create.c | 90 +- .../module_lib/src/txm_thread_delete.c | 66 +- .../src/txm_thread_entry_exit_notify.c | 68 +- .../module_lib/src/txm_thread_identify.c | 68 +- .../module_lib/src/txm_thread_info_get.c | 88 +- .../src/txm_thread_interrupt_control.c | 68 +- .../src/txm_thread_performance_info_get.c | 114 +- .../txm_thread_performance_system_info_get.c | 114 +- .../src/txm_thread_preemption_change.c | 74 +- .../src/txm_thread_priority_change.c | 74 +- .../module_lib/src/txm_thread_relinquish.c | 64 +- .../module_lib/src/txm_thread_reset.c | 66 +- .../module_lib/src/txm_thread_resume.c | 64 +- .../module_lib/src/txm_thread_sleep.c | 64 +- .../src/txm_thread_stack_error_notify.c | 72 +- .../module_lib/src/txm_thread_suspend.c | 68 +- .../module_lib/src/txm_thread_terminate.c | 68 +- .../src/txm_thread_time_slice_change.c | 72 +- .../module_lib/src/txm_thread_wait_abort.c | 64 +- common_modules/module_lib/src/txm_time_get.c | 64 +- common_modules/module_lib/src/txm_time_set.c | 64 +- .../module_lib/src/txm_timer_activate.c | 68 +- .../module_lib/src/txm_timer_change.c | 74 +- .../module_lib/src/txm_timer_create.c | 82 +- .../module_lib/src/txm_timer_deactivate.c | 66 +- .../module_lib/src/txm_timer_delete.c | 68 +- .../module_lib/src/txm_timer_info_get.c | 80 +- .../src/txm_timer_performance_info_get.c | 86 +- .../txm_timer_performance_system_info_get.c | 80 +- .../src/txm_trace_buffer_full_notify.c | 70 +- .../module_lib/src/txm_trace_disable.c | 62 +- .../module_lib/src/txm_trace_enable.c | 70 +- .../module_lib/src/txm_trace_event_filter.c | 64 +- .../module_lib/src/txm_trace_event_unfilter.c | 64 +- .../src/txm_trace_interrupt_control.c | 64 +- .../src/txm_trace_isr_enter_insert.c | 64 +- .../src/txm_trace_isr_exit_insert.c | 64 +- .../src/txm_trace_user_event_insert.c | 70 +- .../inc/txm_module_manager_dispatch.h | 200 +- .../txm_module_manager_application_request.c | 99 +- .../src/txm_module_manager_callback_request.c | 120 +- ...le_manager_event_flags_notify_trampoline.c | 98 +- .../src/txm_module_manager_file_load.c | 2 +- .../src/txm_module_manager_in_place_load.c | 74 +- .../src/txm_module_manager_initialize.c | 90 +- .../src/txm_module_manager_internal_load.c | 170 +- .../src/txm_module_manager_kernel_dispatch.c | 15 +- ...dule_manager_maximum_module_priority_set.c | 100 +- .../src/txm_module_manager_memory_load.c | 2 +- .../src/txm_module_manager_object_allocate.c | 20 +- .../txm_module_manager_object_deallocate.c | 34 +- .../txm_module_manager_object_pointer_get.c | 4 +- ...dule_manager_object_pointer_get_extended.c | 126 +- .../txm_module_manager_object_pool_create.c | 86 +- .../src/txm_module_manager_properties_get.c | 82 +- ...m_module_manager_queue_notify_trampoline.c | 98 +- ...dule_manager_semaphore_notify_trampoline.c | 98 +- .../src/txm_module_manager_start.c | 40 +- ..._module_manager_thread_notify_trampoline.c | 106 +- .../src/txm_module_manager_thread_reset.c | 92 +- ...m_module_manager_timer_notify_trampoline.c | 110 +- .../src/txm_module_manager_unload.c | 102 +- .../src/txm_module_manager_util.c | 358 +- common_smp/inc/tx_api.h | 12 +- .../sample_threadx/tx_initialize_low_level.s | 20 +- ports/arc_em/metaware/inc/tx_port.h | 15 +- .../threadx_regression/.cproject | 157 - .../sample_threadx_regression.cmd | 55 - .../tx_initialize_low_level.s | 360 - .../threadx_regression/vectors.s | 29 - .../metaware/test_regression/tx/.cproject | 141 - .../metaware/test_regression/tx/tx_user.h | 341 - .../test_sandbox/threadx_sandbox/.cproject | 145 - .../sample_threadx_validation.c | 373 - .../sample_threadx_validation.cmd | 55 - .../threadx_sandbox/tx_initialize_low_level.s | 360 - .../test_sandbox/threadx_sandbox/vectors.s | 29 - .../arc_em/metaware/test_sandbox/tx/.cproject | 137 - .../threadx_validation/.cproject | 145 - .../sample_threadx_validation.c | 654 -- .../sample_threadx_validation.cmd | 55 - .../tx_initialize_low_level.s | 360 - .../threadx_validation/vectors.s | 29 - .../metaware/test_validation/tx/.cproject | 137 - .../sample_threadx/sample_threadx.c | 2 +- .../sample_threadx/tx_initialize_low_level.s | 19 +- ports/arc_hs/metaware/inc/tx_port.h | 17 +- .../src/tx_thread_interrupt_control.s | 2 +- ports/arm11/gnu/example_build/libc.a | Bin 1207876 -> 0 bytes ports/arm11/gnu/example_build/libgcc.a | Bin 261004 -> 0 bytes ports/arm9/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes ports/cortex_a15/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_a15/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes .../example_build/sample_threadx/.cproject | 158 + .../example_build/sample_threadx}/.project | 2 +- .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../example_build/sample_threadx/GICv3_gicc.h | 249 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 331 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a34/ac6/example_build/tx/.cproject | 148 + .../ac6/example_build}/tx/.project | 0 ports/cortex_a34/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 98 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a34/ac6/src/tx_thread_fp_disable.c | 94 + .../cortex_a34/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a34/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a34/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 242 + .../example_build/sample_threadx}/.project | 2 +- .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a34/gnu/example_build/tx/.cproject | 234 + .../gnu/example_build}/tx/.project | 0 ports/cortex_a34/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 95 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a34/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a34/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a34/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a34/gnu/src/tx_timer_interrupt.S | 228 + .../ac6/example_build/sample_threadx/GICv3.h | 2 +- .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 61 +- .../example_build/sample_threadx/GICv3_gicd.c | 2 +- .../example_build/sample_threadx/GICv3_gicr.c | 4 +- .../example_build/sample_threadx/MP_Mutexes.S | 2 +- .../example_build/sample_threadx/PPM_AEM.h | 2 +- .../sample_threadx/sample_threadx.c | 102 +- .../sample_threadx/sample_threadx.launch | 96 +- .../sample_threadx/sp804_timer.c | 2 +- .../sample_threadx/sp804_timer.h | 2 +- .../example_build/sample_threadx/startup.S | 6 +- .../example_build/sample_threadx/v8_aarch64.S | 2 +- .../ac6/example_build/sample_threadx/v8_mmu.h | 2 +- .../example_build/sample_threadx/v8_system.h | 2 +- .../example_build/sample_threadx/v8_utils.S | 2 +- .../example_build/sample_threadx/vectors.S | 2 +- ports/cortex_a35/ac6/inc/tx_port.h | 137 +- .../ac6/src/tx_initialize_low_level.S | 95 +- .../ac6/src/tx_thread_context_restore.S | 126 +- .../ac6/src/tx_thread_context_save.S | 119 +- .../cortex_a35/ac6/src/tx_thread_fp_disable.c | 66 +- .../cortex_a35/ac6/src/tx_thread_fp_enable.c | 67 +- .../ac6/src/tx_thread_interrupt_control.S | 82 +- .../ac6/src/tx_thread_interrupt_disable.S | 80 +- .../ac6/src/tx_thread_interrupt_restore.S | 78 +- ports/cortex_a35/ac6/src/tx_thread_schedule.S | 119 +- .../ac6/src/tx_thread_stack_build.S | 86 +- .../ac6/src/tx_thread_system_return.S | 109 +- ports/cortex_a35/ac6/src/tx_timer_interrupt.S | 134 +- .../example_build/sample_threadx/.cproject | 74 +- .../gnu/example_build/sample_threadx/GICv3.h | 2 +- .../sample_threadx/GICv3_aliases.h | 2 +- .../example_build/sample_threadx/GICv3_gicc.h | 2 +- .../example_build/sample_threadx/GICv3_gicd.c | 2 +- .../example_build/sample_threadx/GICv3_gicr.c | 4 +- .../example_build/sample_threadx/MP_Mutexes.S | 2 +- .../example_build/sample_threadx/PPM_AEM.h | 2 +- .../sample_threadx/sample_threadx.c | 102 +- .../sample_threadx/sp804_timer.c | 2 +- .../sample_threadx/sp804_timer.h | 2 +- .../example_build/sample_threadx/startup.S | 2 +- .../example_build/sample_threadx/v8_aarch64.S | 2 +- .../gnu/example_build/sample_threadx/v8_mmu.h | 2 +- .../example_build/sample_threadx/v8_system.h | 2 +- .../example_build/sample_threadx/v8_utils.S | 2 +- .../example_build/sample_threadx/vectors.S | 2 +- .../cortex_a35/gnu/example_build/tx/.cproject | 72 - ports/cortex_a35/gnu/inc/tx_port.h | 137 +- .../gnu/src/tx_initialize_low_level.S | 96 +- .../gnu/src/tx_thread_context_restore.S | 126 +- .../gnu/src/tx_thread_context_save.S | 119 +- .../cortex_a35/gnu/src/tx_thread_fp_disable.c | 66 +- .../cortex_a35/gnu/src/tx_thread_fp_enable.c | 67 +- .../gnu/src/tx_thread_interrupt_control.S | 82 +- .../gnu/src/tx_thread_interrupt_disable.S | 80 +- .../gnu/src/tx_thread_interrupt_restore.S | 78 +- ports/cortex_a35/gnu/src/tx_thread_schedule.S | 119 +- .../gnu/src/tx_thread_stack_build.S | 86 +- .../gnu/src/tx_thread_system_return.S | 109 +- ports/cortex_a35/gnu/src/tx_timer_interrupt.S | 134 +- .../example_build/azure_rtos_workspace.gpj | 0 .../{green => ghs}/example_build/reset.arm | 0 .../ghs}/example_build/sample_threadx.c | 46 +- .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../ghs}/example_build/sample_threadx.ld | 24 +- .../example_build/sample_threadx_el.gpj | 0 .../example_build/sample_threadx_el.ld | 24 +- .../ghs}/example_build/tx.gpj | 71 +- .../example_build/tx_initialize_low_level.arm | 90 +- .../{green => ghs}/example_build/txe.gpj | 71 +- ports/cortex_a5/ghs/inc/tx_el.h | 765 ++ ports/cortex_a5/ghs/inc/tx_ghs.h | 77 + ports/cortex_a5/{green => ghs}/inc/tx_port.h | 96 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_a5/ghs/src/tx_el.c | 1165 +++ ports/cortex_a5/ghs/src/tx_ghs.c | 485 ++ ports/cortex_a5/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 75 +- .../src/tx_thread_context_save.arm | 81 +- .../src/tx_thread_fiq_context_restore.arm | 73 +- .../src/tx_thread_fiq_context_save.arm | 89 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 73 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 71 +- .../src/tx_thread_vectored_context_save.arm | 69 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_a5/ghs/src/txr_ghs.c | 84 + ports/cortex_a5/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_a5/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes .../example_build/sample_threadx/.cproject | 158 + .../example_build/sample_threadx}/.project | 9 +- .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a53/ac6/example_build/tx/.cproject | 148 + .../ac6/example_build}/tx/.project | 0 ports/cortex_a53/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a53/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a53/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a53/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a53/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a53/gnu/example_build/tx/.cproject | 162 + .../cortex_a53/gnu/example_build/tx/.project | 48 + ports/cortex_a53/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a53/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a53/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a53/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a53/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a55/ac6/example_build/tx/.cproject | 148 + .../cortex_a55/ac6/example_build/tx/.project | 48 + ports/cortex_a55/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a55/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a55/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a55/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a55/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a55/gnu/example_build/tx/.cproject | 162 + .../cortex_a55/gnu/example_build/tx/.project | 48 + ports/cortex_a55/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a55/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a55/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a55/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a55/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a57/ac6/example_build/tx/.cproject | 148 + .../cortex_a57/ac6/example_build/tx/.project | 48 + ports/cortex_a57/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a57/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a57/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a57/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a57/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a57/gnu/example_build/tx/.cproject | 162 + .../cortex_a57/gnu/example_build/tx/.project | 48 + ports/cortex_a57/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a57/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a57/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a57/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a57/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a65/ac6/example_build/tx/.cproject | 148 + .../cortex_a65/ac6/example_build/tx/.project | 48 + ports/cortex_a65/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a65/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a65/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a65/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a65/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a65/gnu/example_build/tx/.cproject | 162 + .../cortex_a65/gnu/example_build/tx/.project | 48 + ports/cortex_a65/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a65/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a65/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a65/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a65/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 148 + .../ac6/example_build/tx/.project | 48 + ports/cortex_a65ae/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../cortex_a65ae/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + .../cortex_a65ae/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 162 + .../gnu/example_build/tx/.project | 48 + ports/cortex_a65ae/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../cortex_a65ae/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + .../cortex_a65ae/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/azure_rtos_workspace.gpj | 0 .../{green => ghs}/example_build/reset.arm | 0 .../ghs}/example_build/sample_threadx.c | 46 +- .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../example_build/sample_threadx.ld | 24 +- .../example_build/sample_threadx_el.gpj | 0 .../ghs}/example_build/sample_threadx_el.ld | 24 +- .../ghs}/example_build/tx.gpj | 71 +- .../example_build/tx_initialize_low_level.arm | 90 +- .../ghs}/example_build/txe.gpj | 71 +- ports/cortex_a7/ghs/inc/tx_el.h | 765 ++ ports/cortex_a7/ghs/inc/tx_ghs.h | 77 + ports/cortex_a7/{green => ghs}/inc/tx_port.h | 96 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_a7/ghs/src/tx_el.c | 1165 +++ ports/cortex_a7/ghs/src/tx_ghs.c | 485 ++ ports/cortex_a7/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 75 +- .../src/tx_thread_context_save.arm | 81 +- .../src/tx_thread_fiq_context_restore.arm | 73 +- .../src/tx_thread_fiq_context_save.arm | 89 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 73 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 71 +- .../src/tx_thread_vectored_context_save.arm | 69 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_a7/ghs/src/txr_ghs.c | 84 + ports/cortex_a7/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_a7/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a72/ac6/example_build/tx/.cproject | 148 + .../cortex_a72/ac6/example_build/tx/.project | 48 + ports/cortex_a72/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a72/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a72/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a72/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a72/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a72/gnu/example_build/tx/.cproject | 162 + .../cortex_a72/gnu/example_build/tx/.project | 48 + ports/cortex_a72/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a72/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a72/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a72/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a72/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a73/ac6/example_build/tx/.cproject | 148 + .../cortex_a73/ac6/example_build/tx/.project | 48 + ports/cortex_a73/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a73/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a73/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a73/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a73/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a73/gnu/example_build/tx/.cproject | 162 + .../cortex_a73/gnu/example_build/tx/.project | 48 + ports/cortex_a73/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a73/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a73/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a73/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a73/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a75/ac6/example_build/tx/.cproject | 148 + .../cortex_a75/ac6/example_build/tx/.project | 48 + ports/cortex_a75/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a75/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a75/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a75/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a75/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a75/gnu/example_build/tx/.cproject | 162 + .../cortex_a75/gnu/example_build/tx/.project | 48 + ports/cortex_a75/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a75/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a75/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a75/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a75/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a76/ac6/example_build/tx/.cproject | 148 + .../cortex_a76/ac6/example_build/tx/.project | 48 + ports/cortex_a76/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a76/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a76/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a76/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a76/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a76/gnu/example_build/tx/.cproject | 162 + .../cortex_a76/gnu/example_build/tx/.project | 48 + ports/cortex_a76/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a76/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a76/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a76/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a76/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 148 + .../ac6/example_build/tx/.project | 48 + ports/cortex_a76ae/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../cortex_a76ae/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + .../cortex_a76ae/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 162 + .../gnu/example_build/tx/.project | 48 + ports/cortex_a76ae/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../cortex_a76ae/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + .../cortex_a76ae/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 158 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 325 + .../sample_threadx/sample_threadx.scat | 103 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 779 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a77/ac6/example_build/tx/.cproject | 148 + .../cortex_a77/ac6/example_build/tx/.project | 48 + ports/cortex_a77/ac6/inc/tx_port.h | 379 + .../ac6/src/tx_initialize_low_level.S | 103 + .../ac6/src/tx_thread_context_restore.S | 287 + .../ac6/src/tx_thread_context_save.S | 216 + .../cortex_a77/ac6/src/tx_thread_fp_disable.c | 97 + .../cortex_a77/ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a77/ac6/src/tx_thread_schedule.S | 228 + .../ac6/src/tx_thread_stack_build.S | 158 + .../ac6/src/tx_thread_system_return.S | 151 + ports/cortex_a77/ac6/src/tx_timer_interrupt.S | 228 + .../example_build/sample_threadx/.cproject | 170 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 328 + .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 787 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../cortex_a77/gnu/example_build/tx/.cproject | 162 + .../cortex_a77/gnu/example_build/tx/.project | 48 + ports/cortex_a77/gnu/inc/tx_port.h | 379 + .../gnu/src/tx_initialize_low_level.S | 98 + .../gnu/src/tx_thread_context_restore.S | 287 + .../gnu/src/tx_thread_context_save.S | 216 + .../cortex_a77/gnu/src/tx_thread_fp_disable.c | 97 + .../cortex_a77/gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + ports/cortex_a77/gnu/src/tx_thread_schedule.S | 228 + .../gnu/src/tx_thread_stack_build.S | 158 + .../gnu/src/tx_thread_system_return.S | 151 + ports/cortex_a77/gnu/src/tx_timer_interrupt.S | 228 + .../example_build/azure_rtos_workspace.gpj | 0 .../{green => ghs}/example_build/reset.arm | 0 .../ghs/example_build/sample_threadx.c | 369 + .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../ghs}/example_build/sample_threadx.ld | 24 +- .../example_build/sample_threadx_el.gpj | 0 .../ghs}/example_build/sample_threadx_el.ld | 24 +- .../ghs}/example_build/tx.gpj | 71 +- .../example_build/tx_initialize_low_level.arm | 90 +- .../ghs}/example_build/txe.gpj | 71 +- ports/cortex_a8/ghs/inc/tx_el.h | 765 ++ ports/cortex_a8/ghs/inc/tx_ghs.h | 77 + ports/cortex_a8/{green => ghs}/inc/tx_port.h | 96 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_a8/ghs/src/tx_el.c | 1165 +++ ports/cortex_a8/ghs/src/tx_ghs.c | 485 ++ ports/cortex_a8/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 77 +- .../src/tx_thread_context_save.arm | 81 +- .../src/tx_thread_fiq_context_restore.arm | 75 +- .../src/tx_thread_fiq_context_save.arm | 89 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 73 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 69 +- .../src/tx_thread_vectored_context_save.arm | 69 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_a8/ghs/src/txr_ghs.c | 84 + ports/cortex_a8/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_a8/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes .../example_build/azure_rtos_workspace.gpj | 0 .../{green => ghs}/example_build/reset.arm | 0 .../ghs/example_build/sample_threadx.c | 369 + .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../ghs}/example_build/sample_threadx.ld | 24 +- .../example_build/sample_threadx_el.gpj | 0 .../example_build/sample_threadx_el.ld | 24 +- .../{green => ghs}/example_build/tx.gpj | 71 +- .../example_build/tx_initialize_low_level.arm | 90 +- .../ghs}/example_build/txe.gpj | 71 +- ports/cortex_a9/ghs/inc/tx_el.h | 765 ++ ports/cortex_a9/ghs/inc/tx_ghs.h | 77 + ports/cortex_a9/{green => ghs}/inc/tx_port.h | 96 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_a9/ghs/src/tx_el.c | 1165 +++ ports/cortex_a9/ghs/src/tx_ghs.c | 485 ++ ports/cortex_a9/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 75 +- .../src/tx_thread_context_save.arm | 81 +- .../src/tx_thread_fiq_context_restore.arm | 75 +- .../src/tx_thread_fiq_context_save.arm | 89 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 73 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 71 +- .../src/tx_thread_vectored_context_save.arm | 69 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_a9/ghs/src/txr_ghs.c | 84 + ports/cortex_a9/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_a9/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes ports/cortex_m0/gnu/example_build/libgcc.a | Bin 1551842 -> 0 bytes ports/cortex_m3/ac5/inc/tx_port.h | 25 +- ports/cortex_m3/ac5/src/tx_timer_interrupt.s | 9 +- ports/cortex_m3/ac6/inc/tx_port.h | 25 +- ports/cortex_m3/ac6/src/tx_timer_interrupt.S | 9 +- .../ghs/example_build/sample_threadx.ld | 24 +- .../ghs/example_build/sample_threadx_el.ld | 24 +- .../example_build/tx_initialize_low_level.arm | 84 +- ports/cortex_m3/ghs/inc/tx_el.h | 42 +- ports/cortex_m3/ghs/inc/tx_port.h | 68 +- .../ghs/src/tx_thread_interrupt_disable.arm | 2 +- .../ghs/src/tx_thread_interrupt_restore.arm | 2 +- .../cortex_m3/ghs/src/tx_thread_schedule.arm | 20 +- .../ghs/src/tx_thread_system_return.arm | 2 +- .../cortex_m3/ghs/src/tx_timer_interrupt.arm | 6 +- ports/cortex_m3/gnu/example_build/libc.a | Bin 743432 -> 0 bytes ports/cortex_m3/gnu/inc/tx_port.h | 25 +- ports/cortex_m3/gnu/src/tx_thread_schedule.S | 13 +- ports/cortex_m3/gnu/src/tx_timer_interrupt.S | 9 +- ports/cortex_m3/iar/inc/tx_port.h | 25 +- ports/cortex_m3/iar/src/tx_timer_interrupt.s | 9 +- ports/cortex_m3/keil/inc/tx_port.h | 25 +- ports/cortex_m33/ac6/inc/tx_port.h | 464 +- .../cortex_m33/ac6/inc/tx_secure_interface.h | 2 +- .../ac6/src/tx_thread_secure_stack.c | 215 +- ports/cortex_m33/gnu/inc/tx_port.h | 453 +- .../cortex_m33/gnu/inc/tx_secure_interface.h | 2 +- .../gnu/src/tx_initialize_low_level.S | 6 +- ports/cortex_m33/gnu/src/tx_thread_schedule.S | 10 +- .../gnu/src/tx_thread_secure_stack.c | 213 +- ports/cortex_m33/iar/inc/tx_port.h | 424 +- .../cortex_m33/iar/inc/tx_secure_interface.h | 2 +- .../iar/src/tx_thread_secure_stack.c | 212 +- ports/cortex_m4/ac5/inc/tx_port.h | 25 +- ports/cortex_m4/ac5/src/tx_timer_interrupt.s | 9 +- ports/cortex_m4/ac6/inc/tx_port.h | 25 +- ports/cortex_m4/ac6/src/tx_timer_interrupt.S | 9 +- .../ghs/example_build/sample_threadx.ld | 24 +- .../ghs/example_build/sample_threadx_el.ld | 24 +- ports/cortex_m4/gnu/example_build/libc.a | Bin 743432 -> 0 bytes ports/cortex_m4/gnu/inc/tx_port.h | 25 +- ports/cortex_m4/gnu/src/tx_thread_schedule.S | 13 +- ports/cortex_m4/gnu/src/tx_timer_interrupt.S | 9 +- ports/cortex_m4/iar/inc/tx_port.h | 25 +- ports/cortex_m4/iar/src/tx_timer_interrupt.s | 9 +- ports/cortex_m4/keil/inc/tx_port.h | 25 +- ports/cortex_m7/ac5/inc/tx_port.h | 25 +- ports/cortex_m7/ac5/src/tx_timer_interrupt.s | 9 +- ports/cortex_m7/ac6/inc/tx_port.h | 25 +- ports/cortex_m7/ac6/src/tx_timer_interrupt.S | 9 +- .../ghs/example_build/sample_threadx.ld | 24 +- .../ghs/example_build/sample_threadx_el.ld | 24 +- ports/cortex_m7/gnu/example_build/libc.a | Bin 743432 -> 0 bytes ports/cortex_m7/gnu/inc/tx_port.h | 25 +- ports/cortex_m7/gnu/src/tx_thread_schedule.S | 13 +- ports/cortex_m7/gnu/src/tx_timer_interrupt.S | 9 +- ports/cortex_m7/iar/inc/tx_port.h | 25 +- ports/cortex_m7/iar/src/tx_timer_interrupt.s | 9 +- .../example_build/azure_rtos_workspace.gpj | 0 .../{green => ghs}/example_build/reset.arm | 0 .../ghs/example_build/sample_threadx.c | 369 + .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../ghs/example_build/sample_threadx.ld | 44 + .../example_build/sample_threadx_el.gpj | 0 .../ghs/example_build/sample_threadx_el.ld | 45 + ports/cortex_r4/ghs/example_build/tx.gpj | 222 + .../example_build/tx_initialize_low_level.arm | 319 + ports/cortex_r4/ghs/example_build/txe.gpj | 223 + ports/cortex_r4/ghs/inc/tx_el.h | 765 ++ ports/cortex_r4/ghs/inc/tx_ghs.h | 77 + ports/cortex_r4/{green => ghs}/inc/tx_port.h | 92 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_r4/ghs/src/tx_el.c | 1165 +++ ports/cortex_r4/ghs/src/tx_ghs.c | 485 ++ ports/cortex_r4/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 70 +- .../src/tx_thread_context_save.arm | 76 +- .../src/tx_thread_fiq_context_restore.arm | 68 +- .../src/tx_thread_fiq_context_save.arm | 84 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 66 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 64 +- .../src/tx_thread_vectored_context_save.arm | 64 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_r4/ghs/src/txr_ghs.c | 84 + ports/cortex_r4/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_r4/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes .../green/example_build/sample_threadx.c | 369 - .../green/example_build/sample_threadx.ld | 44 - .../green/example_build/sample_threadx_el.ld | 45 - ports/cortex_r4/green/example_build/tx.gpj | 283 - .../example_build/tx_initialize_low_level.arm | 319 - ports/cortex_r4/green/example_build/txe.gpj | 284 - .../example_build/azure_rtos_workspace.gpj | 0 .../{green => ghs}/example_build/reset.arm | 0 .../ghs/example_build/sample_threadx.c | 369 + .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../ghs/example_build/sample_threadx.ld | 44 + .../example_build/sample_threadx_el.gpj | 0 .../ghs/example_build/sample_threadx_el.ld | 45 + ports/cortex_r5/ghs/example_build/tx.gpj | 222 + .../example_build/tx_initialize_low_level.arm | 319 + ports/cortex_r5/ghs/example_build/txe.gpj | 223 + ports/cortex_r5/ghs/inc/tx_el.h | 765 ++ ports/cortex_r5/ghs/inc/tx_ghs.h | 77 + ports/cortex_r5/{green => ghs}/inc/tx_port.h | 96 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_r5/ghs/src/tx_el.c | 1165 +++ ports/cortex_r5/ghs/src/tx_ghs.c | 485 ++ ports/cortex_r5/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 70 +- .../src/tx_thread_context_save.arm | 76 +- .../src/tx_thread_fiq_context_restore.arm | 70 +- .../src/tx_thread_fiq_context_save.arm | 84 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 68 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 66 +- .../src/tx_thread_vectored_context_save.arm | 64 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_r5/ghs/src/txr_ghs.c | 84 + ports/cortex_r5/gnu/example_build/libc.a | Bin 2447586 -> 0 bytes ports/cortex_r5/gnu/example_build/libgcc.a | Bin 260152 -> 0 bytes .../green/example_build/sample_threadx.c | 369 - .../green/example_build/sample_threadx.ld | 44 - .../green/example_build/sample_threadx_el.ld | 45 - ports/cortex_r5/green/example_build/tx.gpj | 283 - .../example_build/tx_initialize_low_level.arm | 319 - ports/cortex_r5/green/example_build/txe.gpj | 284 - .../example_build/azure_rtos_workspace.gpj | 4 +- .../{green => ghs}/example_build/reset.arm | 0 .../ghs/example_build/sample_threadx.c | 369 + .../example_build/sample_threadx.con | 2 +- .../example_build/sample_threadx.gpj | 0 .../ghs/example_build/sample_threadx.ld | 44 + .../example_build/sample_threadx_el.gpj | 0 .../ghs/example_build/sample_threadx_el.ld | 45 + ports/cortex_r7/ghs/example_build/tx.gpj | 222 + .../example_build/tx_initialize_low_level.arm | 319 + ports/cortex_r7/ghs/example_build/txe.gpj | 223 + ports/cortex_r7/ghs/inc/tx_el.h | 765 ++ ports/cortex_r7/ghs/inc/tx_ghs.h | 77 + ports/cortex_r7/{green => ghs}/inc/tx_port.h | 96 +- .../{green => ghs}/readme_threadx.txt | 0 ports/cortex_r7/ghs/src/tx_el.c | 1165 +++ ports/cortex_r7/ghs/src/tx_ghs.c | 485 ++ ports/cortex_r7/ghs/src/tx_ghse.c | 49 + .../src/tx_thread_context_restore.arm | 70 +- .../src/tx_thread_context_save.arm | 76 +- .../src/tx_thread_fiq_context_restore.arm | 70 +- .../src/tx_thread_fiq_context_save.arm | 84 +- .../src/tx_thread_fiq_nesting_end.arm | 76 +- .../src/tx_thread_fiq_nesting_start.arm | 70 +- .../src/tx_thread_interrupt_control.arm | 58 +- .../src/tx_thread_interrupt_disable.arm | 56 +- .../src/tx_thread_interrupt_restore.arm | 54 +- .../src/tx_thread_irq_nesting_end.arm | 76 +- .../src/tx_thread_irq_nesting_start.arm | 70 +- .../{green => ghs}/src/tx_thread_schedule.arm | 68 +- .../src/tx_thread_stack_build.arm | 52 +- .../src/tx_thread_system_return.arm | 66 +- .../src/tx_thread_vectored_context_save.arm | 64 +- .../{green => ghs}/src/tx_timer_interrupt.arm | 70 +- ports/cortex_r7/ghs/src/txr_ghs.c | 84 + .../green/example_build/sample_threadx.c | 369 - .../green/example_build/sample_threadx.ld | 44 - .../green/example_build/sample_threadx_el.ld | 45 - ports/cortex_r7/green/example_build/tx.gpj | 283 - .../example_build/tx_initialize_low_level.arm | 319 - ports/cortex_r7/green/example_build/txe.gpj | 284 - .../module_lib/src/txm_module_initialize.S | 2 + .../module_lib/src/txm_module_initialize.S | 2 + .../cortex_a7/ac5/example_build/build_all.bat | 7 - .../example_build/sample_threadx/.cproject | 170 + .../ac6/example_build/sample_threadx/.project | 27 + .../example_build/sample_threadx/exceptions.c | 92 + .../sample_threadx}/sample_threadx.c | 3 +- .../sample_threadx/sample_threadx.launch | 181 + .../sample_threadx/sample_threadx.scat | 44 + .../sample_threadx/tx_initialize_low_level.S | 238 + .../sample_threadx_module/.cproject | 218 + .../sample_threadx_module/.project | 27 + .../sample_threadx_module.c | 432 ++ .../txm_module_preamble.S | 62 + .../sample_threadx_module_manager/.cproject | 172 + .../sample_threadx_module_manager/.project | 27 + .../exceptions.c | 92 + .../sample_threadx.scat | 44 + .../sample_threadx_module_manager.c | 126 + .../sample_threadx_module_manager.launch | 337 + .../tx_initialize_low_level.S | 232 + .../cortex_m0+/ac6/example_build/tx/.cproject | 166 + .../cortex_m0+/ac6/example_build/tx/.project | 63 + .../ac6/example_build/txm/.cproject | 184 + .../cortex_m0+/ac6/example_build/txm/.project | 58 + ports_module/cortex_m0+/ac6/inc/tx_port.h | 523 ++ .../cortex_m0+/ac6/inc/txm_module_port.h | 381 + .../module_lib/src/txm_module_initialize.S | 120 + .../src/txm_module_thread_shell_entry.c | 173 + .../src/tx_thread_context_restore.S | 83 + .../src/tx_thread_context_save.S | 84 + .../src/tx_thread_interrupt_control.S | 81 + .../src/tx_thread_interrupt_disable.S | 76 + .../src/tx_thread_interrupt_restore.S | 72 + .../module_manager/src/tx_thread_schedule.S | 564 ++ .../src/tx_thread_stack_build.S | 134 + .../src/tx_thread_system_return.S | 87 + .../module_manager/src/tx_timer_interrupt.S | 242 + .../src/txm_module_manager_alignment_adjust.c | 448 ++ ...xm_module_manager_external_memory_enable.c | 295 + .../txm_module_manager_memory_fault_handler.c | 110 + .../txm_module_manager_memory_fault_notify.c | 84 + .../txm_module_manager_mm_register_setup.c | 797 ++ .../txm_module_manager_thread_stack_build.S | 140 + .../example_build/sample_threadx/.cproject | 166 + .../gnu/example_build/sample_threadx/.project | 27 + .../sample_threadx/cortexm_crt0.s | 127 + .../sample_threadx}/sample_threadx.c | 3 +- .../sample_threadx/sample_threadx.launch | 206 + .../sample_threadx/sample_threadx.ld | 203 + .../sample_threadx/tx_initialize_low_level.S | 240 + .../sample_threadx/tx_simulator_startup.s | 73 + .../sample_threadx_module/.cproject | 178 + .../sample_threadx_module/.project | 27 + .../sample_threadx_module/gcc_setup.s | 131 + .../sample_threadx_module.c | 432 ++ .../sample_threadx_module.ld | 215 + .../txm_module_preamble.S | 60 + .../sample_threadx_module_manager/.cproject | 174 + .../sample_threadx_module_manager/.project | 27 + .../cortexm_crt0.s | 127 + .../sample_threadx_module_manager/libgcc.a | Bin 0 -> 1525572 bytes .../sample_threadx.ld | 203 + .../sample_threadx_module_manager.c | 127 + .../sample_threadx_module_manager.launch | 376 + .../tx_initialize_low_level.S | 233 + .../tx_simulator_startup.s | 73 + .../cortex_m0+/gnu/example_build/tx/.cproject | 164 + .../cortex_m0+/gnu/example_build/tx/.project | 63 + .../gnu/example_build/txm/.cproject | 168 + .../cortex_m0+/gnu/example_build/txm/.project | 58 + ports_module/cortex_m0+/gnu/inc/tx_port.h | 537 ++ .../cortex_m0+/gnu/inc/txm_module_port.h | 381 + .../src/txm_module_thread_shell_entry.c | 173 + .../src/tx_thread_context_restore.S | 83 + .../src/tx_thread_context_save.S | 84 + .../src/tx_thread_interrupt_control.S | 81 + .../src/tx_thread_interrupt_disable.S | 76 + .../src/tx_thread_interrupt_restore.S | 72 + .../module_manager/src/tx_thread_schedule.S | 564 ++ .../src/tx_thread_stack_build.S | 134 + .../src/tx_thread_system_return.S | 87 + .../module_manager/src/tx_timer_interrupt.S | 242 + .../src/txm_module_manager_alignment_adjust.c | 448 ++ ...xm_module_manager_external_memory_enable.c | 295 + .../txm_module_manager_memory_fault_handler.c | 110 + .../txm_module_manager_memory_fault_notify.c | 84 + .../txm_module_manager_mm_register_setup.c | 797 ++ .../txm_module_manager_thread_stack_build.S | 140 + .../iar/example_build/azure_rtos.eww | 19 + .../cortex_m0+/iar/example_build/cstartup_M.s | 73 + .../iar/example_build/sample_threadx.c | 385 + .../iar/example_build/sample_threadx.ewd | 2974 ++++++++ .../iar/example_build/sample_threadx.ewp | 2137 ++++++ .../iar/example_build/sample_threadx.icf | 34 + .../iar/example_build/sample_threadx_module.c | 432 ++ .../example_build/sample_threadx_module.ewd | 2974 ++++++++ .../example_build/sample_threadx_module.ewp | 2135 ++++++ .../example_build/sample_threadx_module.icf | 53 + .../sample_threadx_module_manager.c | 125 + .../sample_threadx_module_manager.ewd | 2974 ++++++++ .../sample_threadx_module_manager.ewp | 2140 ++++++ .../sample_threadx_module_manager.icf | 36 + .../sample_threadx_module_manager.dnx | 105 + .../cortex_m0+/iar/example_build/startup.s | 622 ++ .../cortex_m0+/iar/example_build/tx.ewp | 2867 ++++++++ .../example_build/tx_initialize_low_level.s | 185 + .../cortex_m0+/iar/example_build/txm.ewp | 2477 +++++++ .../iar/example_build/txm_module_preamble.s | 69 + ports_module/cortex_m0+/iar/inc/tx_port.h | 578 ++ .../cortex_m0+/iar/inc/txm_module_port.h | 381 + .../src/txm_module_thread_shell_entry.c | 173 + .../iar/module_manager/src/tx_iar.c | 804 ++ .../iar/module_manager/src/tx_misra.s | 1074 +++ .../src/tx_thread_context_restore.S | 79 + .../src/tx_thread_context_save.S | 81 + .../src/tx_thread_interrupt_control.S | 80 + .../src/tx_thread_interrupt_disable.S | 75 + .../src/tx_thread_interrupt_restore.S | 71 + .../module_manager/src/tx_thread_schedule.S | 548 ++ .../src/tx_thread_stack_build.S | 133 + .../src/tx_thread_system_return.S | 85 + .../module_manager/src/tx_timer_interrupt.S | 254 + .../src/txm_module_manager_alignment_adjust.c | 448 ++ ...xm_module_manager_external_memory_enable.c | 295 + .../txm_module_manager_memory_fault_handler.c | 110 + .../txm_module_manager_memory_fault_notify.c | 84 + .../txm_module_manager_mm_register_setup.c | 797 ++ .../txm_module_manager_thread_stack_build.S | 139 + .../ac6/example_build/ThreadX_Library.uvoptx | 48 +- .../ac6/example_build/ThreadX_Library.uvprojx | 20 +- .../cortex_m23/ac6/inc/tx_secure_interface.h | 3 +- .../cortex_m23/ac6/inc/txm_module_port.h | 14 +- .../module_lib/src/txm_module_initialize.S | 49 +- .../src/txm_module_thread_shell_entry.c | 27 +- .../cortex_m3/ac5/example_build/build.bat | 8 - .../cortex_m3/ac5/example_build/clean.bat | 2 - .../cortex_m3/ac5/example_build/setenv.bat | 13 - .../module_manager/src/tx_timer_interrupt.s | 9 +- .../cortex_m3/ac6/example_build/all.bat | 5 - .../cortex_m3/ac6/example_build/build.bat | 24 - .../cortex_m3/ac6/example_build/clean.bat | 4 - .../cortex_m3/ac6/example_build/initws.bat | 14 - .../cortex_m3/ac6/example_build/setenv.bat | 16 - .../cortex_m3/ac6/inc/txm_module_port.h | 10 +- .../module_lib/src/txm_module_initialize.S | 38 +- .../src/txm_module_thread_shell_entry.c | 15 +- .../module_manager/src/tx_timer_interrupt.S | 9 +- .../module_manager/src/tx_thread_schedule.S | 22 +- .../module_manager/src/tx_timer_interrupt.S | 9 +- ports_module/cortex_m3/iar/inc/tx_port.h | 2 +- .../cortex_m3/iar/inc/txm_module_port.h | 2 +- .../module_manager/src/tx_timer_interrupt.s | 9 +- .../ac6/example_build/ThreadX_Library.uvoptx | 48 +- .../ac6/example_build/ThreadX_Library.uvprojx | 20 +- ports_module/cortex_m33/ac6/inc/tx_port.h | 569 +- .../cortex_m33/ac6/inc/tx_secure_interface.h | 5 +- .../cortex_m33/ac6/inc/txm_module_port.h | 14 +- .../module_lib/src/txm_module_initialize.S | 45 +- .../src/txm_module_thread_shell_entry.c | 33 +- ports_module/cortex_m33/gnu/inc/tx_port.h | 404 +- .../cortex_m33/gnu/inc/tx_secure_interface.h | 3 +- .../cortex_m33/gnu/inc/txm_module_port.h | 8 +- .../src/tx_initialize_low_level.S | 28 +- .../module_manager/src/tx_thread_schedule.S | 12 +- .../src/tx_thread_secure_stack_initialize.S | 79 + ports_module/cortex_m33/iar/inc/tx_port.h | 404 +- .../cortex_m33/iar/inc/tx_secure_interface.h | 3 +- .../cortex_m33/iar/inc/txm_module_port.h | 8 +- .../cortex_m4/ac5/example_build/build.bat | 8 - .../cortex_m4/ac5/example_build/clean.bat | 2 - .../cortex_m4/ac5/example_build/setenv.bat | 13 - .../module_manager/src/tx_timer_interrupt.s | 9 +- .../cortex_m4/ac6/example_build/all.bat | 5 - .../cortex_m4/ac6/example_build/build.bat | 24 - .../cortex_m4/ac6/example_build/clean.bat | 4 - .../cortex_m4/ac6/example_build/initws.bat | 14 - .../cortex_m4/ac6/example_build/setenv.bat | 16 - .../cortex_m4/ac6/inc/txm_module_port.h | 10 +- .../module_lib/src/txm_module_initialize.S | 38 +- .../src/txm_module_thread_shell_entry.c | 15 +- .../module_manager/src/tx_timer_interrupt.S | 9 +- .../cortex_m4/gnu/example_build/build_all.bat | 5 - .../module_manager/src/tx_thread_schedule.S | 22 +- .../module_manager/src/tx_timer_interrupt.S | 9 +- ports_module/cortex_m4/iar/inc/tx_port.h | 2 +- .../cortex_m4/iar/inc/txm_module_port.h | 2 +- .../module_manager/src/tx_timer_interrupt.s | 9 +- .../cortex_m7/ac5/example_build/build.bat | 8 - .../cortex_m7/ac5/example_build/clean.bat | 2 - .../cortex_m7/ac5/example_build/setenv.bat | 13 - .../module_manager/src/tx_timer_interrupt.s | 9 +- .../cortex_m7/ac6/example_build/all.bat | 5 - .../cortex_m7/ac6/example_build/build.bat | 24 - .../cortex_m7/ac6/example_build/clean.bat | 4 - .../cortex_m7/ac6/example_build/initws.bat | 14 - .../cortex_m7/ac6/example_build/setenv.bat | 16 - .../cortex_m7/ac6/inc/txm_module_port.h | 10 +- .../module_lib/src/txm_module_initialize.S | 38 +- .../src/txm_module_thread_shell_entry.c | 15 +- .../module_manager/src/tx_timer_interrupt.S | 9 +- .../cortex_m7/gnu/example_build/build_all.bat | 5 - .../module_manager/src/tx_thread_schedule.S | 22 +- .../module_manager/src/tx_timer_interrupt.S | 9 +- ports_module/cortex_m7/iar/inc/tx_port.h | 2 +- .../cortex_m7/iar/inc/txm_module_port.h | 2 +- .../module_manager/src/tx_timer_interrupt.s | 9 +- .../example_build/tx_initialize_low_level.s | 4 +- ports_module/rxv2/iar/inc/tx_port.h | 10 +- ports_module/rxv2/iar/inc/txm_module_port.h | 6 +- .../src/tx_initialize_low_level.s | 4 +- .../src/tx_thread_context_restore.s | 62 +- .../src/tx_thread_context_save.s | 62 +- .../src/tx_thread_interrupt_control.s | 22 +- .../module_manager/src/tx_thread_schedule.s | 25 +- .../src/tx_thread_stack_build.s | 55 +- .../src/tx_thread_system_return.s | 6 +- .../module_manager/src/tx_timer_interrupt.s | 14 +- .../txm_module_manager_thread_stack_build.s | 4 +- .../metaware/example_build/.metadata/.lock | 0 .../metaware/example_build/.metadata/.log | 3457 --------- .../dialog_settings.xml | 4 - .../.plugins/org.eclipse.cdt.core/.log | 27 - .../sample_threadx.1443481736829.pdom | Bin 344064 -> 0 bytes .../sample_threadx.1592510892863.pdom | Bin 397312 -> 0 bytes .../sample_threadx.language.settings.xml | 4787 ------------ .../org.eclipse.cdt.core/shareddefaults.xml | 1 - .../tx.1443481396650.pdom | Bin 806912 -> 0 bytes .../tx.language.settings.xml | 6515 ----------------- .../org.eclipse.cdt.make.core/specs.c | 1 - .../org.eclipse.cdt.make.core/specs.cpp | 1 - .../spec.c | 0 .../spec.cpp | 0 .../org.eclipse.cdt.ui/cHelpSettings.xml | 6 - .../org.eclipse.cdt.ui/dialog_settings.xml | 11 - .../f0/60943445c62300171ed8c82249418230 | 444 -- .../sample_threadx/.indexes/properties.index | Bin 411 -> 0 bytes .../.projects/sample_threadx/.markers.snap | Bin 32 -> 0 bytes .../.projects/sample_threadx/.syncinfo.snap | Bin 32 -> 0 bytes .../.projects/tx/.indexes/properties.index | Bin 335 -> 0 bytes .../.projects/tx/.markers.snap | Bin 32 -> 0 bytes .../.projects/tx/.syncinfo.snap | Bin 32 -> 0 bytes .../.root/.indexes/history.version | 1 - .../.root/.indexes/properties.index | Bin 104 -> 0 bytes .../.root/.indexes/properties.version | 1 - .../.root/.markers.snap | Bin 32 -> 0 bytes .../.safetable/org.eclipse.core.resources | Bin 930 -> 0 bytes .../org.eclipse.core.resources/27.snap | Bin 25958 -> 0 bytes .../.settings/org.eclipse.cdt.core.prefs | 2 - ....eclipse.cdt.core.prj-sample_threadx.prefs | 2 - .../org.eclipse.cdt.core.prj-tx.prefs | 2 - .../org.eclipse.cdt.debug.core.prefs | 3 - .../.settings/org.eclipse.cdt.debug.ui.prefs | 7 - .../org.eclipse.cdt.launchbar.core.prefs | 5 - .../org.eclipse.cdt.managedbuilder.core.prefs | 5 - .../.settings/org.eclipse.cdt.ui.prefs | 4 - ...rg.eclipse.cdt.ui.prj-sample_threadx.prefs | 3 - .../.settings/org.eclipse.cdt.ui.prj-tx.prefs | 3 - .../org.eclipse.core.resources.prefs | 2 - .../.settings/org.eclipse.debug.core.prefs | 5 - .../.settings/org.eclipse.debug.ui.prefs | 9 - .../.settings/org.eclipse.ui.editors.prefs | 2 - .../.settings/org.eclipse.ui.ide.prefs | 5 - .../.settings/org.eclipse.ui.prefs | 2 - .../.settings/org.eclipse.ui.workbench.prefs | 3 - .../.launches/sample_threadx Debug.launch | 476 -- .../org.eclipse.debug.ui/dialog_settings.xml | 11 - .../launchConfigurationHistory.xml | 23 - .../org.eclipse.e4.workbench/workbench.xmi | 2041 ------ .../.workspace/2017/4/15/refactorings.history | 3 - .../.workspace/2017/4/15/refactorings.index | 8 - .../.workspace/2020/6/25/refactorings.history | 3 - .../.workspace/2020/6/25/refactorings.index | 3 - .../dialog_settings.xml | 7 - .../dialog_settings.xml | 5 - .../org.eclipse.ui.ide/dialog_settings.xml | 22 - .../dialog_settings.xml | 31 - .../org.eclipse.ui.workbench/workingsets.xml | 4 - .../example_build/.metadata/version.ini | 3 - .../.settings/language.settings.xml | 35 - .../org.eclipse.cdt.codan.core.prefs | 69 - .../tx/.settings/language.settings.xml | 35 - .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 409 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 160 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a34_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 83 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S} | 165 +- .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a34_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 83 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 163 +- .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/GICv3_gicr.c | 21 +- .../example_build/sample_threadx/MP_Mutexes.S | 49 +- .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../sample_threadx/sample_threadx.c | 22 +- .../sample_threadx/sample_threadx.launch | 80 +- .../example_build/sample_threadx/startup.S | 10 +- .../sample_threadx/timer_interrupts.c | 18 +- .../example_build/sample_threadx/v8_aarch64.S | 20 +- .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 12 +- .../ac6/example_build/tx/.cproject | 36 +- ports_smp/cortex_a35_smp/ac6/inc/tx_port.h | 16 +- .../ac6/src/tx_initialize_low_level.S | 8 +- .../ac6/src/tx_thread_context_restore.S | 10 +- .../ac6/src/tx_thread_context_save.S | 12 +- .../ac6/src/tx_thread_fp_disable.c | 8 +- .../ac6/src/tx_thread_fp_enable.c | 8 +- .../ac6/src/tx_thread_interrupt_control.S | 9 +- .../ac6/src/tx_thread_interrupt_disable.S | 8 +- .../ac6/src/tx_thread_interrupt_restore.S | 8 +- .../ac6/src/tx_thread_schedule.S | 10 +- .../ac6/src/tx_thread_smp_core_get.S | 6 +- .../ac6/src/tx_thread_smp_core_preempt.S | 11 +- .../ac6/src/tx_thread_smp_current_state_get.S | 6 +- .../src/tx_thread_smp_current_thread_get.S | 6 +- .../ac6/src/tx_thread_smp_initialize_wait.S | 6 +- .../src/tx_thread_smp_low_level_initialize.S | 6 +- .../ac6/src/tx_thread_smp_protect.S | 6 +- .../ac6/src/tx_thread_smp_time_get.S | 6 +- .../ac6/src/tx_thread_smp_unprotect.S | 6 +- .../ac6/src/tx_thread_stack_build.S | 8 +- .../ac6/src/tx_thread_system_return.S | 10 +- .../ac6/src/tx_timer_interrupt.S | 4 +- .../example_build/sample_threadx/GICv3_gicr.c | 21 +- .../example_build/sample_threadx/MP_Mutexes.S | 49 +- .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../sample_threadx/sample_threadx.c | 22 +- .../sample_threadx/timer_interrupts.c | 18 +- .../example_build/sample_threadx/v8_aarch64.S | 20 +- .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 12 +- .../gnu/example_build/tx/.cproject | 2 +- ports_smp/cortex_a35_smp/gnu/inc/tx_port.h | 13 +- .../gnu/src/tx_initialize_low_level.S | 8 +- .../gnu/src/tx_thread_context_restore.S | 10 +- .../gnu/src/tx_thread_context_save.S | 12 +- .../gnu/src/tx_thread_fp_disable.c | 8 +- .../gnu/src/tx_thread_fp_enable.c | 8 +- .../gnu/src/tx_thread_interrupt_control.S | 9 +- .../gnu/src/tx_thread_interrupt_disable.S | 8 +- .../gnu/src/tx_thread_interrupt_restore.S | 8 +- .../gnu/src/tx_thread_schedule.S | 10 +- .../gnu/src/tx_thread_smp_core_get.S | 8 +- .../gnu/src/tx_thread_smp_core_preempt.S | 9 +- .../gnu/src/tx_thread_smp_current_state_get.S | 8 +- .../src/tx_thread_smp_current_thread_get.S | 8 +- .../gnu/src/tx_thread_smp_initialize_wait.S | 8 +- .../src/tx_thread_smp_low_level_initialize.S | 8 +- .../gnu/src/tx_thread_smp_protect.S | 8 +- .../gnu/src/tx_thread_smp_time_get.S | 8 +- .../gnu/src/tx_thread_smp_unprotect.S | 8 +- .../gnu/src/tx_thread_stack_build.S | 8 +- .../gnu/src/tx_thread_system_return.S | 10 +- .../gnu/src/tx_timer_interrupt.S | 6 +- .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a53_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 163 +- .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a53_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S} | 165 +- .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a55_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a55_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a57_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a57_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../ac6/src/tx_thread_timeout.c | 166 - .../gnu/src/tx_thread_timeout.c | 166 - .../green/example_build/tx/libtx.gpj | 2 +- .../green/src/tx_thread_timeout.c | 165 - .../iar/src/tx_thread_timeout.c | 164 - .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a65_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a65_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a65ae_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a65ae_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a72_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a72_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a73_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a73_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a75_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a75_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a76_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a76_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a76ae_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a76ae_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 413 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 192 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a77_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 86 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a77_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 86 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 148 + .../ac6/example_build/sample_threadx/.project | 26 + .../ac6/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 409 ++ .../sample_threadx/sample_threadx.scat | 262 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 803 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../ac6/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../ac6/example_build/tx/.cproject | 160 + .../ac6/example_build/tx/.project | 48 + ports_smp/cortex_a78_smp/ac6/inc/tx_port.h | 429 ++ .../ac6/src/tx_initialize_low_level.S | 104 + .../ac6/src/tx_thread_context_restore.S | 390 + .../ac6/src/tx_thread_context_save.S | 254 + .../ac6/src/tx_thread_fp_disable.c | 97 + .../ac6/src/tx_thread_fp_enable.c | 96 + .../ac6/src/tx_thread_interrupt_control.S | 81 + .../ac6/src/tx_thread_interrupt_disable.S | 79 + .../ac6/src/tx_thread_interrupt_restore.S | 77 + .../ac6/src/tx_thread_schedule.S | 304 + .../ac6/src/tx_thread_smp_core_get.S | 85 + .../ac6/src/tx_thread_smp_core_preempt.S | 83 + .../ac6/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../ac6/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../ac6/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../ac6/src/tx_thread_smp_time_get.S | 70 + .../ac6/src/tx_thread_smp_unprotect.S | 126 + .../ac6/src/tx_thread_stack_build.S | 160 + .../ac6/src/tx_thread_system_return.S | 189 + .../ac6/src/tx_timer_interrupt.S | 193 + .../example_build/sample_threadx/.cproject | 164 + .../gnu/example_build/sample_threadx/.project | 26 + .../gnu/example_build/sample_threadx/GICv3.h | 561 ++ .../sample_threadx/GICv3_aliases.h | 113 + .../example_build/sample_threadx/GICv3_gicc.h | 254 + .../example_build/sample_threadx/GICv3_gicd.c | 339 + .../example_build/sample_threadx/GICv3_gicr.c | 308 + .../example_build/sample_threadx/MP_Mutexes.S | 133 + .../example_build/sample_threadx/MP_Mutexes.h | 66 + .../example_build/sample_threadx/PPM_AEM.h | 66 + .../sample_threadx/sample_threadx.c | 393 + .../sample_threadx/sample_threadx.launch | 445 ++ .../sample_threadx/sample_threadx.ld | 245 + .../sample_threadx/sp804_timer.c | 122 + .../sample_threadx/sp804_timer.h | 53 + .../example_build/sample_threadx/startup.S | 798 ++ .../sample_threadx/timer_interrupts.c | 152 + .../sample_threadx/use_model_semihosting.ds | 1 + .../example_build/sample_threadx/v8_aarch64.S | 179 + .../example_build/sample_threadx/v8_aarch64.h | 103 + .../gnu/example_build/sample_threadx/v8_mmu.h | 128 + .../example_build/sample_threadx/v8_system.h | 115 + .../example_build/sample_threadx/v8_utils.S | 69 + .../example_build/sample_threadx/vectors.S | 252 + .../gnu/example_build/tx/.cproject | 194 + .../gnu/example_build/tx/.project | 48 + ports_smp/cortex_a78_smp/gnu/inc/tx_port.h | 429 ++ .../gnu/src/tx_initialize_low_level.S | 102 + .../gnu/src/tx_thread_context_restore.S | 390 + .../gnu/src/tx_thread_context_save.S | 254 + .../gnu/src/tx_thread_fp_disable.c | 97 + .../gnu/src/tx_thread_fp_enable.c | 96 + .../gnu/src/tx_thread_interrupt_control.S | 81 + .../gnu/src/tx_thread_interrupt_disable.S | 79 + .../gnu/src/tx_thread_interrupt_restore.S | 77 + .../gnu/src/tx_thread_schedule.S | 304 + .../gnu/src/tx_thread_smp_core_get.S | 85 + .../gnu/src/tx_thread_smp_core_preempt.S | 83 + .../gnu/src/tx_thread_smp_current_state_get.S | 90 + .../src/tx_thread_smp_current_thread_get.S | 90 + .../gnu/src/tx_thread_smp_initialize_wait.S | 139 + .../src/tx_thread_smp_low_level_initialize.S | 70 + .../gnu/src/tx_thread_smp_protect.S | 373 + ...x_thread_smp_protection_wait_list_macros.h | 302 + .../gnu/src/tx_thread_smp_time_get.S | 70 + .../gnu/src/tx_thread_smp_unprotect.S | 126 + .../gnu/src/tx_thread_stack_build.S | 160 + .../gnu/src/tx_thread_system_return.S | 189 + .../gnu/src/tx_timer_interrupt.S | 193 + .../FreeRTOS/tx_freertos.c | 56 +- 3111 files changed, 495735 insertions(+), 40800 deletions(-) delete mode 100644 ports/arc_em/metaware/test_regression/threadx_regression/.cproject delete mode 100644 ports/arc_em/metaware/test_regression/threadx_regression/sample_threadx_regression.cmd delete mode 100644 ports/arc_em/metaware/test_regression/threadx_regression/tx_initialize_low_level.s delete mode 100644 ports/arc_em/metaware/test_regression/threadx_regression/vectors.s delete mode 100644 ports/arc_em/metaware/test_regression/tx/.cproject delete mode 100644 ports/arc_em/metaware/test_regression/tx/tx_user.h delete mode 100644 ports/arc_em/metaware/test_sandbox/threadx_sandbox/.cproject delete mode 100644 ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.c delete mode 100644 ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.cmd delete mode 100644 ports/arc_em/metaware/test_sandbox/threadx_sandbox/tx_initialize_low_level.s delete mode 100644 ports/arc_em/metaware/test_sandbox/threadx_sandbox/vectors.s delete mode 100644 ports/arc_em/metaware/test_sandbox/tx/.cproject delete mode 100644 ports/arc_em/metaware/test_validation/threadx_validation/.cproject delete mode 100644 ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.c delete mode 100644 ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.cmd delete mode 100644 ports/arc_em/metaware/test_validation/threadx_validation/tx_initialize_low_level.s delete mode 100644 ports/arc_em/metaware/test_validation/threadx_validation/vectors.s delete mode 100644 ports/arc_em/metaware/test_validation/tx/.cproject delete mode 100644 ports/arm11/gnu/example_build/libc.a delete mode 100644 ports/arm11/gnu/example_build/libgcc.a delete mode 100644 ports/arm9/gnu/example_build/libgcc.a delete mode 100644 ports/cortex_a15/gnu/example_build/libc.a delete mode 100644 ports/cortex_a15/gnu/example_build/libgcc.a create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/.cproject rename ports/{arc_em/metaware/test_sandbox/threadx_sandbox => cortex_a34/ac6/example_build/sample_threadx}/.project (94%) create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a34/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a34/ac6/example_build/tx/.cproject rename ports/{arc_em/metaware/test_regression => cortex_a34/ac6/example_build}/tx/.project (100%) create mode 100644 ports/cortex_a34/ac6/inc/tx_port.h create mode 100644 ports/cortex_a34/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a34/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a34/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a34/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a34/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/.cproject rename ports/{arc_em/metaware/test_validation/threadx_validation => cortex_a34/gnu/example_build/sample_threadx}/.project (94%) create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a34/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a34/gnu/example_build/tx/.cproject rename ports/{arc_em/metaware/test_sandbox => cortex_a34/gnu/example_build}/tx/.project (100%) create mode 100644 ports/cortex_a34/gnu/inc/tx_port.h create mode 100644 ports/cortex_a34/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a34/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a34/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a34/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a34/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_aliases.h rename ports/cortex_a5/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_a5/{green => ghs}/example_build/reset.arm (100%) rename ports/{cortex_a7/green => cortex_a5/ghs}/example_build/sample_threadx.c (93%) rename ports/cortex_a5/{green => ghs}/example_build/sample_threadx.con (87%) rename ports/cortex_a5/{green => ghs}/example_build/sample_threadx.gpj (100%) rename ports/{cortex_a8/green => cortex_a5/ghs}/example_build/sample_threadx.ld (81%) rename ports/cortex_a5/{green => ghs}/example_build/sample_threadx_el.gpj (100%) rename ports/cortex_a5/{green => ghs}/example_build/sample_threadx_el.ld (82%) rename ports/{cortex_a8/green => cortex_a5/ghs}/example_build/tx.gpj (72%) rename ports/{cortex_a7/green => cortex_a5/ghs}/example_build/tx_initialize_low_level.arm (94%) rename ports/cortex_a5/{green => ghs}/example_build/txe.gpj (72%) create mode 100644 ports/cortex_a5/ghs/inc/tx_el.h create mode 100644 ports/cortex_a5/ghs/inc/tx_ghs.h rename ports/cortex_a5/{green => ghs}/inc/tx_port.h (91%) rename ports/cortex_a5/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_a5/ghs/src/tx_el.c create mode 100644 ports/cortex_a5/ghs/src/tx_ghs.c create mode 100644 ports/cortex_a5/ghs/src/tx_ghse.c rename ports/cortex_a5/{green => ghs}/src/tx_thread_context_restore.arm (94%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_context_save.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_fiq_context_restore.arm (94%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_fiq_context_save.arm (91%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_schedule.arm (94%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_system_return.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_thread_vectored_context_save.arm (92%) rename ports/cortex_a5/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_a5/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_a5/gnu/example_build/libc.a delete mode 100644 ports/cortex_a5/gnu/example_build/libgcc.a create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/.cproject rename ports/{arc_em/metaware/test_regression/threadx_regression => cortex_a53/ac6/example_build/sample_threadx}/.project (77%) create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a53/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a53/ac6/example_build/tx/.cproject rename ports/{arc_em/metaware/test_validation => cortex_a53/ac6/example_build}/tx/.project (100%) create mode 100644 ports/cortex_a53/ac6/inc/tx_port.h create mode 100644 ports/cortex_a53/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a53/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a53/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a53/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a53/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a53/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a53/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a53/gnu/example_build/tx/.project create mode 100644 ports/cortex_a53/gnu/inc/tx_port.h create mode 100644 ports/cortex_a53/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a53/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a53/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a53/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a53/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a55/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a55/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a55/ac6/example_build/tx/.project create mode 100644 ports/cortex_a55/ac6/inc/tx_port.h create mode 100644 ports/cortex_a55/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a55/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a55/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a55/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a55/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a55/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a55/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a55/gnu/example_build/tx/.project create mode 100644 ports/cortex_a55/gnu/inc/tx_port.h create mode 100644 ports/cortex_a55/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a55/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a55/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a55/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a55/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a57/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a57/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a57/ac6/example_build/tx/.project create mode 100644 ports/cortex_a57/ac6/inc/tx_port.h create mode 100644 ports/cortex_a57/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a57/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a57/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a57/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a57/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a57/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a57/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a57/gnu/example_build/tx/.project create mode 100644 ports/cortex_a57/gnu/inc/tx_port.h create mode 100644 ports/cortex_a57/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a57/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a57/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a57/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a57/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a65/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a65/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a65/ac6/example_build/tx/.project create mode 100644 ports/cortex_a65/ac6/inc/tx_port.h create mode 100644 ports/cortex_a65/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a65/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a65/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a65/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a65/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a65/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a65/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a65/gnu/example_build/tx/.project create mode 100644 ports/cortex_a65/gnu/inc/tx_port.h create mode 100644 ports/cortex_a65/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a65/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a65/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a65/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a65/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a65ae/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a65ae/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a65ae/ac6/example_build/tx/.project create mode 100644 ports/cortex_a65ae/ac6/inc/tx_port.h create mode 100644 ports/cortex_a65ae/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a65ae/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a65ae/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a65ae/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a65ae/gnu/example_build/tx/.project create mode 100644 ports/cortex_a65ae/gnu/inc/tx_port.h create mode 100644 ports/cortex_a65ae/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a65ae/gnu/src/tx_timer_interrupt.S rename ports/cortex_a7/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_a7/{green => ghs}/example_build/reset.arm (100%) rename ports/{cortex_a5/green => cortex_a7/ghs}/example_build/sample_threadx.c (93%) rename ports/cortex_a7/{green => ghs}/example_build/sample_threadx.con (87%) rename ports/cortex_a7/{green => ghs}/example_build/sample_threadx.gpj (100%) rename ports/cortex_a7/{green => ghs}/example_build/sample_threadx.ld (81%) rename ports/cortex_a7/{green => ghs}/example_build/sample_threadx_el.gpj (100%) rename ports/{cortex_a8/green => cortex_a7/ghs}/example_build/sample_threadx_el.ld (82%) rename ports/{cortex_a5/green => cortex_a7/ghs}/example_build/tx.gpj (72%) rename ports/{cortex_a9/green => cortex_a7/ghs}/example_build/tx_initialize_low_level.arm (94%) rename ports/{cortex_a8/green => cortex_a7/ghs}/example_build/txe.gpj (72%) create mode 100644 ports/cortex_a7/ghs/inc/tx_el.h create mode 100644 ports/cortex_a7/ghs/inc/tx_ghs.h rename ports/cortex_a7/{green => ghs}/inc/tx_port.h (91%) rename ports/cortex_a7/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_a7/ghs/src/tx_el.c create mode 100644 ports/cortex_a7/ghs/src/tx_ghs.c create mode 100644 ports/cortex_a7/ghs/src/tx_ghse.c rename ports/cortex_a7/{green => ghs}/src/tx_thread_context_restore.arm (94%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_context_save.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_fiq_context_restore.arm (94%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_fiq_context_save.arm (91%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_schedule.arm (94%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_system_return.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_thread_vectored_context_save.arm (92%) rename ports/cortex_a7/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_a7/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_a7/gnu/example_build/libc.a delete mode 100644 ports/cortex_a7/gnu/example_build/libgcc.a create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a72/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a72/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a72/ac6/example_build/tx/.project create mode 100644 ports/cortex_a72/ac6/inc/tx_port.h create mode 100644 ports/cortex_a72/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a72/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a72/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a72/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a72/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a72/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a72/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a72/gnu/example_build/tx/.project create mode 100644 ports/cortex_a72/gnu/inc/tx_port.h create mode 100644 ports/cortex_a72/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a72/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a72/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a72/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a72/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a73/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a73/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a73/ac6/example_build/tx/.project create mode 100644 ports/cortex_a73/ac6/inc/tx_port.h create mode 100644 ports/cortex_a73/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a73/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a73/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a73/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a73/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a73/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a73/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a73/gnu/example_build/tx/.project create mode 100644 ports/cortex_a73/gnu/inc/tx_port.h create mode 100644 ports/cortex_a73/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a73/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a73/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a73/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a73/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a75/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a75/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a75/ac6/example_build/tx/.project create mode 100644 ports/cortex_a75/ac6/inc/tx_port.h create mode 100644 ports/cortex_a75/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a75/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a75/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a75/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a75/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a75/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a75/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a75/gnu/example_build/tx/.project create mode 100644 ports/cortex_a75/gnu/inc/tx_port.h create mode 100644 ports/cortex_a75/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a75/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a75/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a75/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a75/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a76/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a76/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a76/ac6/example_build/tx/.project create mode 100644 ports/cortex_a76/ac6/inc/tx_port.h create mode 100644 ports/cortex_a76/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a76/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a76/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a76/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a76/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a76/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a76/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a76/gnu/example_build/tx/.project create mode 100644 ports/cortex_a76/gnu/inc/tx_port.h create mode 100644 ports/cortex_a76/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a76/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a76/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a76/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a76/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a76ae/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a76ae/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a76ae/ac6/example_build/tx/.project create mode 100644 ports/cortex_a76ae/ac6/inc/tx_port.h create mode 100644 ports/cortex_a76ae/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a76ae/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a76ae/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a76ae/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a76ae/gnu/example_build/tx/.project create mode 100644 ports/cortex_a76ae/gnu/inc/tx_port.h create mode 100644 ports/cortex_a76ae/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a76ae/gnu/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/.project create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a77/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a77/ac6/example_build/tx/.cproject create mode 100644 ports/cortex_a77/ac6/example_build/tx/.project create mode 100644 ports/cortex_a77/ac6/inc/tx_port.h create mode 100644 ports/cortex_a77/ac6/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_context_save.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a77/ac6/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a77/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_schedule.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a77/ac6/src/tx_thread_system_return.S create mode 100644 ports/cortex_a77/ac6/src/tx_timer_interrupt.S create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/.cproject create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/.project create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/startup.S create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports/cortex_a77/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports/cortex_a77/gnu/example_build/tx/.cproject create mode 100644 ports/cortex_a77/gnu/example_build/tx/.project create mode 100644 ports/cortex_a77/gnu/inc/tx_port.h create mode 100644 ports/cortex_a77/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_context_restore.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_context_save.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_fp_disable.c create mode 100644 ports/cortex_a77/gnu/src/tx_thread_fp_enable.c create mode 100644 ports/cortex_a77/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_schedule.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_stack_build.S create mode 100644 ports/cortex_a77/gnu/src/tx_thread_system_return.S create mode 100644 ports/cortex_a77/gnu/src/tx_timer_interrupt.S rename ports/cortex_a8/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_a8/{green => ghs}/example_build/reset.arm (100%) create mode 100644 ports/cortex_a8/ghs/example_build/sample_threadx.c rename ports/cortex_a8/{green => ghs}/example_build/sample_threadx.con (87%) rename ports/cortex_a8/{green => ghs}/example_build/sample_threadx.gpj (100%) rename ports/{cortex_a9/green => cortex_a8/ghs}/example_build/sample_threadx.ld (81%) rename ports/cortex_a8/{green => ghs}/example_build/sample_threadx_el.gpj (100%) rename ports/{cortex_a7/green => cortex_a8/ghs}/example_build/sample_threadx_el.ld (82%) rename ports/{cortex_a7/green => cortex_a8/ghs}/example_build/tx.gpj (72%) rename ports/cortex_a8/{green => ghs}/example_build/tx_initialize_low_level.arm (95%) rename ports/{cortex_a9/green => cortex_a8/ghs}/example_build/txe.gpj (72%) create mode 100644 ports/cortex_a8/ghs/inc/tx_el.h create mode 100644 ports/cortex_a8/ghs/inc/tx_ghs.h rename ports/cortex_a8/{green => ghs}/inc/tx_port.h (91%) rename ports/cortex_a8/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_a8/ghs/src/tx_el.c create mode 100644 ports/cortex_a8/ghs/src/tx_ghs.c create mode 100644 ports/cortex_a8/ghs/src/tx_ghse.c rename ports/cortex_a8/{green => ghs}/src/tx_thread_context_restore.arm (94%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_context_save.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_fiq_context_restore.arm (94%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_fiq_context_save.arm (91%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_schedule.arm (94%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_system_return.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_thread_vectored_context_save.arm (92%) rename ports/cortex_a8/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_a8/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_a8/gnu/example_build/libc.a delete mode 100644 ports/cortex_a8/gnu/example_build/libgcc.a rename ports/cortex_a9/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_a9/{green => ghs}/example_build/reset.arm (100%) create mode 100644 ports/cortex_a9/ghs/example_build/sample_threadx.c rename ports/cortex_a9/{green => ghs}/example_build/sample_threadx.con (87%) rename ports/cortex_a9/{green => ghs}/example_build/sample_threadx.gpj (100%) rename ports/{cortex_a5/green => cortex_a9/ghs}/example_build/sample_threadx.ld (81%) rename ports/cortex_a9/{green => ghs}/example_build/sample_threadx_el.gpj (100%) rename ports/cortex_a9/{green => ghs}/example_build/sample_threadx_el.ld (82%) rename ports/cortex_a9/{green => ghs}/example_build/tx.gpj (72%) rename ports/{cortex_a5/green => cortex_a9/ghs}/example_build/tx_initialize_low_level.arm (94%) rename ports/{cortex_a7/green => cortex_a9/ghs}/example_build/txe.gpj (72%) create mode 100644 ports/cortex_a9/ghs/inc/tx_el.h create mode 100644 ports/cortex_a9/ghs/inc/tx_ghs.h rename ports/cortex_a9/{green => ghs}/inc/tx_port.h (91%) rename ports/cortex_a9/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_a9/ghs/src/tx_el.c create mode 100644 ports/cortex_a9/ghs/src/tx_ghs.c create mode 100644 ports/cortex_a9/ghs/src/tx_ghse.c rename ports/cortex_a9/{green => ghs}/src/tx_thread_context_restore.arm (94%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_context_save.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_fiq_context_restore.arm (94%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_fiq_context_save.arm (91%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_schedule.arm (94%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_system_return.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_thread_vectored_context_save.arm (92%) rename ports/cortex_a9/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_a9/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_a9/gnu/example_build/libc.a delete mode 100644 ports/cortex_a9/gnu/example_build/libgcc.a delete mode 100644 ports/cortex_m0/gnu/example_build/libgcc.a delete mode 100644 ports/cortex_m3/gnu/example_build/libc.a delete mode 100644 ports/cortex_m4/gnu/example_build/libc.a delete mode 100644 ports/cortex_m7/gnu/example_build/libc.a rename ports/cortex_r4/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_r4/{green => ghs}/example_build/reset.arm (100%) create mode 100644 ports/cortex_r4/ghs/example_build/sample_threadx.c rename ports/cortex_r4/{green => ghs}/example_build/sample_threadx.con (87%) rename ports/cortex_r4/{green => ghs}/example_build/sample_threadx.gpj (100%) create mode 100644 ports/cortex_r4/ghs/example_build/sample_threadx.ld rename ports/cortex_r4/{green => ghs}/example_build/sample_threadx_el.gpj (100%) create mode 100644 ports/cortex_r4/ghs/example_build/sample_threadx_el.ld create mode 100644 ports/cortex_r4/ghs/example_build/tx.gpj create mode 100644 ports/cortex_r4/ghs/example_build/tx_initialize_low_level.arm create mode 100644 ports/cortex_r4/ghs/example_build/txe.gpj create mode 100644 ports/cortex_r4/ghs/inc/tx_el.h create mode 100644 ports/cortex_r4/ghs/inc/tx_ghs.h rename ports/cortex_r4/{green => ghs}/inc/tx_port.h (92%) rename ports/cortex_r4/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_r4/ghs/src/tx_el.c create mode 100644 ports/cortex_r4/ghs/src/tx_ghs.c create mode 100644 ports/cortex_r4/ghs/src/tx_ghse.c rename ports/cortex_r4/{green => ghs}/src/tx_thread_context_restore.arm (96%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_context_save.arm (94%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_fiq_context_restore.arm (96%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_fiq_context_save.arm (93%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_schedule.arm (94%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_system_return.arm (94%) rename ports/cortex_r4/{green => ghs}/src/tx_thread_vectored_context_save.arm (95%) rename ports/cortex_r4/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_r4/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_r4/gnu/example_build/libc.a delete mode 100644 ports/cortex_r4/gnu/example_build/libgcc.a delete mode 100644 ports/cortex_r4/green/example_build/sample_threadx.c delete mode 100644 ports/cortex_r4/green/example_build/sample_threadx.ld delete mode 100644 ports/cortex_r4/green/example_build/sample_threadx_el.ld delete mode 100644 ports/cortex_r4/green/example_build/tx.gpj delete mode 100644 ports/cortex_r4/green/example_build/tx_initialize_low_level.arm delete mode 100644 ports/cortex_r4/green/example_build/txe.gpj rename ports/cortex_r5/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_r5/{green => ghs}/example_build/reset.arm (100%) create mode 100644 ports/cortex_r5/ghs/example_build/sample_threadx.c rename ports/cortex_r5/{green => ghs}/example_build/sample_threadx.con (87%) rename ports/cortex_r5/{green => ghs}/example_build/sample_threadx.gpj (100%) create mode 100644 ports/cortex_r5/ghs/example_build/sample_threadx.ld rename ports/cortex_r5/{green => ghs}/example_build/sample_threadx_el.gpj (100%) create mode 100644 ports/cortex_r5/ghs/example_build/sample_threadx_el.ld create mode 100644 ports/cortex_r5/ghs/example_build/tx.gpj create mode 100644 ports/cortex_r5/ghs/example_build/tx_initialize_low_level.arm create mode 100644 ports/cortex_r5/ghs/example_build/txe.gpj create mode 100644 ports/cortex_r5/ghs/inc/tx_el.h create mode 100644 ports/cortex_r5/ghs/inc/tx_ghs.h rename ports/cortex_r5/{green => ghs}/inc/tx_port.h (91%) rename ports/cortex_r5/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_r5/ghs/src/tx_el.c create mode 100644 ports/cortex_r5/ghs/src/tx_ghs.c create mode 100644 ports/cortex_r5/ghs/src/tx_ghse.c rename ports/cortex_r5/{green => ghs}/src/tx_thread_context_restore.arm (96%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_context_save.arm (94%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_fiq_context_restore.arm (96%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_fiq_context_save.arm (93%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_schedule.arm (96%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_system_return.arm (94%) rename ports/cortex_r5/{green => ghs}/src/tx_thread_vectored_context_save.arm (95%) rename ports/cortex_r5/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_r5/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_r5/gnu/example_build/libc.a delete mode 100644 ports/cortex_r5/gnu/example_build/libgcc.a delete mode 100644 ports/cortex_r5/green/example_build/sample_threadx.c delete mode 100644 ports/cortex_r5/green/example_build/sample_threadx.ld delete mode 100644 ports/cortex_r5/green/example_build/sample_threadx_el.ld delete mode 100644 ports/cortex_r5/green/example_build/tx.gpj delete mode 100644 ports/cortex_r5/green/example_build/tx_initialize_low_level.arm delete mode 100644 ports/cortex_r5/green/example_build/txe.gpj rename ports/cortex_r7/{green => ghs}/example_build/azure_rtos_workspace.gpj (100%) rename ports/cortex_r7/{green => ghs}/example_build/reset.arm (100%) create mode 100644 ports/cortex_r7/ghs/example_build/sample_threadx.c rename ports/cortex_r7/{green => ghs}/example_build/sample_threadx.con (88%) rename ports/cortex_r7/{green => ghs}/example_build/sample_threadx.gpj (100%) create mode 100644 ports/cortex_r7/ghs/example_build/sample_threadx.ld rename ports/cortex_r7/{green => ghs}/example_build/sample_threadx_el.gpj (100%) create mode 100644 ports/cortex_r7/ghs/example_build/sample_threadx_el.ld create mode 100644 ports/cortex_r7/ghs/example_build/tx.gpj create mode 100644 ports/cortex_r7/ghs/example_build/tx_initialize_low_level.arm create mode 100644 ports/cortex_r7/ghs/example_build/txe.gpj create mode 100644 ports/cortex_r7/ghs/inc/tx_el.h create mode 100644 ports/cortex_r7/ghs/inc/tx_ghs.h rename ports/cortex_r7/{green => ghs}/inc/tx_port.h (91%) rename ports/cortex_r7/{green => ghs}/readme_threadx.txt (100%) create mode 100644 ports/cortex_r7/ghs/src/tx_el.c create mode 100644 ports/cortex_r7/ghs/src/tx_ghs.c create mode 100644 ports/cortex_r7/ghs/src/tx_ghse.c rename ports/cortex_r7/{green => ghs}/src/tx_thread_context_restore.arm (96%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_context_save.arm (94%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_fiq_context_restore.arm (96%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_fiq_context_save.arm (93%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_fiq_nesting_end.arm (91%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_fiq_nesting_start.arm (92%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_interrupt_control.arm (92%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_interrupt_disable.arm (92%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_interrupt_restore.arm (92%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_irq_nesting_end.arm (91%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_irq_nesting_start.arm (92%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_schedule.arm (96%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_stack_build.arm (96%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_system_return.arm (94%) rename ports/cortex_r7/{green => ghs}/src/tx_thread_vectored_context_save.arm (95%) rename ports/cortex_r7/{green => ghs}/src/tx_timer_interrupt.arm (95%) create mode 100644 ports/cortex_r7/ghs/src/txr_ghs.c delete mode 100644 ports/cortex_r7/green/example_build/sample_threadx.c delete mode 100644 ports/cortex_r7/green/example_build/sample_threadx.ld delete mode 100644 ports/cortex_r7/green/example_build/sample_threadx_el.ld delete mode 100644 ports/cortex_r7/green/example_build/tx.gpj delete mode 100644 ports/cortex_r7/green/example_build/tx_initialize_low_level.arm delete mode 100644 ports/cortex_r7/green/example_build/txe.gpj delete mode 100644 ports_module/cortex_a7/ac5/example_build/build_all.bat create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx/.project create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx/exceptions.c rename {ports/cortex_a9/green/example_build => ports_module/cortex_m0+/ac6/example_build/sample_threadx}/sample_threadx.c (98%) create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx/tx_initialize_low_level.S create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.cproject create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.project create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/sample_threadx_module.c create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/txm_module_preamble.S create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.cproject create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.project create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/exceptions.c create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx.scat create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch create mode 100644 ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/tx_initialize_low_level.S create mode 100644 ports_module/cortex_m0+/ac6/example_build/tx/.cproject create mode 100644 ports_module/cortex_m0+/ac6/example_build/tx/.project create mode 100644 ports_module/cortex_m0+/ac6/example_build/txm/.cproject create mode 100644 ports_module/cortex_m0+/ac6/example_build/txm/.project create mode 100644 ports_module/cortex_m0+/ac6/inc/tx_port.h create mode 100644 ports_module/cortex_m0+/ac6/inc/txm_module_port.h create mode 100644 ports_module/cortex_m0+/ac6/module_lib/src/txm_module_initialize.S create mode 100644 ports_module/cortex_m0+/ac6/module_lib/src/txm_module_thread_shell_entry.c create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_restore.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_save.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_control.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_disable.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_restore.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_schedule.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_stack_build.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_system_return.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/tx_timer_interrupt.S create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_alignment_adjust.c create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_external_memory_enable.c create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_handler.c create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_notify.c create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_mm_register_setup.c create mode 100644 ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_thread_stack_build.S create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/.project create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/cortexm_crt0.s rename {ports/cortex_a8/green/example_build => ports_module/cortex_m0+/gnu/example_build/sample_threadx}/sample_threadx.c (98%) create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_initialize_low_level.S create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_simulator_startup.s create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.cproject create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.project create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/gcc_setup.s create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.c create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.ld create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/txm_module_preamble.S create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.cproject create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.project create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/cortexm_crt0.s create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/libgcc.a create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx.ld create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_initialize_low_level.S create mode 100644 ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_simulator_startup.s create mode 100644 ports_module/cortex_m0+/gnu/example_build/tx/.cproject create mode 100644 ports_module/cortex_m0+/gnu/example_build/tx/.project create mode 100644 ports_module/cortex_m0+/gnu/example_build/txm/.cproject create mode 100644 ports_module/cortex_m0+/gnu/example_build/txm/.project create mode 100644 ports_module/cortex_m0+/gnu/inc/tx_port.h create mode 100644 ports_module/cortex_m0+/gnu/inc/txm_module_port.h create mode 100644 ports_module/cortex_m0+/gnu/module_lib/src/txm_module_thread_shell_entry.c create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_restore.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_save.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_control.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_disable.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_restore.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_schedule.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_stack_build.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_system_return.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/tx_timer_interrupt.S create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_alignment_adjust.c create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_external_memory_enable.c create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_handler.c create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_notify.c create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_mm_register_setup.c create mode 100644 ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_thread_stack_build.S create mode 100644 ports_module/cortex_m0+/iar/example_build/azure_rtos.eww create mode 100644 ports_module/cortex_m0+/iar/example_build/cstartup_M.s create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx.c create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx.ewd create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx.ewp create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx.icf create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module.c create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewd create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewp create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module.icf create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.c create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewd create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewp create mode 100644 ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.icf create mode 100644 ports_module/cortex_m0+/iar/example_build/settings/sample_threadx_module_manager.dnx create mode 100644 ports_module/cortex_m0+/iar/example_build/startup.s create mode 100644 ports_module/cortex_m0+/iar/example_build/tx.ewp create mode 100644 ports_module/cortex_m0+/iar/example_build/tx_initialize_low_level.s create mode 100644 ports_module/cortex_m0+/iar/example_build/txm.ewp create mode 100644 ports_module/cortex_m0+/iar/example_build/txm_module_preamble.s create mode 100644 ports_module/cortex_m0+/iar/inc/tx_port.h create mode 100644 ports_module/cortex_m0+/iar/inc/txm_module_port.h create mode 100644 ports_module/cortex_m0+/iar/module_lib/src/txm_module_thread_shell_entry.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_iar.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_misra.s create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_restore.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_save.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_control.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_disable.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_restore.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_schedule.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_stack_build.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_thread_system_return.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/tx_timer_interrupt.S create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_alignment_adjust.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_external_memory_enable.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_handler.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_notify.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_mm_register_setup.c create mode 100644 ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_thread_stack_build.S delete mode 100644 ports_module/cortex_m3/ac5/example_build/build.bat delete mode 100644 ports_module/cortex_m3/ac5/example_build/clean.bat delete mode 100644 ports_module/cortex_m3/ac5/example_build/setenv.bat delete mode 100644 ports_module/cortex_m3/ac6/example_build/all.bat delete mode 100644 ports_module/cortex_m3/ac6/example_build/build.bat delete mode 100644 ports_module/cortex_m3/ac6/example_build/clean.bat delete mode 100644 ports_module/cortex_m3/ac6/example_build/initws.bat delete mode 100644 ports_module/cortex_m3/ac6/example_build/setenv.bat create mode 100644 ports_module/cortex_m33/gnu/module_manager/src/tx_thread_secure_stack_initialize.S delete mode 100644 ports_module/cortex_m4/ac5/example_build/build.bat delete mode 100644 ports_module/cortex_m4/ac5/example_build/clean.bat delete mode 100644 ports_module/cortex_m4/ac5/example_build/setenv.bat delete mode 100644 ports_module/cortex_m4/ac6/example_build/all.bat delete mode 100644 ports_module/cortex_m4/ac6/example_build/build.bat delete mode 100644 ports_module/cortex_m4/ac6/example_build/clean.bat delete mode 100644 ports_module/cortex_m4/ac6/example_build/initws.bat delete mode 100644 ports_module/cortex_m4/ac6/example_build/setenv.bat delete mode 100644 ports_module/cortex_m4/gnu/example_build/build_all.bat delete mode 100644 ports_module/cortex_m7/ac5/example_build/build.bat delete mode 100644 ports_module/cortex_m7/ac5/example_build/clean.bat delete mode 100644 ports_module/cortex_m7/ac5/example_build/setenv.bat delete mode 100644 ports_module/cortex_m7/ac6/example_build/all.bat delete mode 100644 ports_module/cortex_m7/ac6/example_build/build.bat delete mode 100644 ports_module/cortex_m7/ac6/example_build/clean.bat delete mode 100644 ports_module/cortex_m7/ac6/example_build/initws.bat delete mode 100644 ports_module/cortex_m7/ac6/example_build/setenv.bat delete mode 100644 ports_module/cortex_m7/gnu/example_build/build_all.bat delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.lock delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.log delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.codan.ui/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/.log delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.1443481736829.pdom delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.1592510892863.pdom delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.language.settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/shareddefaults.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.1443481396650.pdom delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.language.settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.managedbuilder.core/spec.c delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.managedbuilder.core/spec.cpp delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/cHelpSettings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.history/f0/60943445c62300171ed8c82249418230 delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.indexes/properties.index delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.markers.snap delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.syncinfo.snap delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/tx/.indexes/properties.index delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/tx/.markers.snap delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/tx/.syncinfo.snap delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/history.version delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.index delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.markers.snap delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.safetable/org.eclipse.core.resources delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/27.snap delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-sample_threadx.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-tx.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.ui.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.launchbar.core.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.managedbuilder.core.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-sample_threadx.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-tx.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.core.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.ui.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.core/.launches/sample_threadx Debug.launch delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/launchConfigurationHistory.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.history delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.index delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.history delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.index delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.ui.refactoring/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.editors/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/.metadata/version.ini delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/language.settings.xml delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/org.eclipse.cdt.codan.core.prefs delete mode 100644 ports_smp/arc_hs_smp/metaware/example_build/tx/.settings/language.settings.xml create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a34_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a34_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_thread_get.S rename ports_smp/{cortex_a35_smp/gnu/src/tx_thread_timeout.c => cortex_a34_smp/ac6/src/tx_thread_smp_initialize_wait.S} (51%) create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a34_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a34_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a34_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_thread_get.S rename ports_module/cortex_a35_smp/ac6/module_manager/src/tx_thread_timeout.c => ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_initialize_wait.S (51%) create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a34_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a53_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a53_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_thread_get.S rename ports_module/cortex_a35_smp/gnu/module_manager/src/tx_thread_timeout.c => ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_initialize_wait.S (51%) create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a53_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a53_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a53_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_thread_get.S rename ports_smp/{cortex_a35_smp/ac6/src/tx_thread_timeout.c => cortex_a53_smp/gnu/src/tx_thread_smp_initialize_wait.S} (51%) create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a53_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a55_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a55_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a55_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a55_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a55_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a55_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a57_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a57_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a57_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a57_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a57_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a57_smp/gnu/src/tx_timer_interrupt.S delete mode 100644 ports_smp/cortex_a5x_smp/ac6/src/tx_thread_timeout.c delete mode 100644 ports_smp/cortex_a5x_smp/gnu/src/tx_thread_timeout.c delete mode 100644 ports_smp/cortex_a5x_smp/green/src/tx_thread_timeout.c delete mode 100644 ports_smp/cortex_a5x_smp/iar/src/tx_thread_timeout.c create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a65_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a65_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a65_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a65_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a65_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a65_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a65ae_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a65ae_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a65ae_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a65ae_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a72_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a72_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a72_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a72_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a72_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a72_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a73_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a73_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a73_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a73_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a73_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a73_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a75_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a75_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a75_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a75_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a75_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a75_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a76_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a76_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a76_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a76_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a76_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a76_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a76ae_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a76ae_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a76ae_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a76ae_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a77_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a77_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a77_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a77_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a77_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a77_smp/gnu/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.scat create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a78_smp/ac6/example_build/tx/.project create mode 100644 ports_smp/cortex_a78_smp/ac6/inc/tx_port.h create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a78_smp/ac6/src/tx_timer_interrupt.S create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.cproject create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.project create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_aliases.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicc.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicd.c create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicr.c create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.S create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/PPM_AEM.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.c create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.launch create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.ld create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.c create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/startup.S create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/timer_interrupts.c create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.S create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_mmu.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_system.h create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_utils.S create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/vectors.S create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/tx/.cproject create mode 100644 ports_smp/cortex_a78_smp/gnu/example_build/tx/.project create mode 100644 ports_smp/cortex_a78_smp/gnu/inc/tx_port.h create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_initialize_low_level.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_restore.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_save.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_disable.c create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_enable.c create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_control.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_disable.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_restore.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_schedule.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_get.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_preempt.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_state_get.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_thread_get.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_initialize_wait.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_low_level_initialize.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protect.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_time_get.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_unprotect.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_stack_build.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_thread_system_return.S create mode 100644 ports_smp/cortex_a78_smp/gnu/src/tx_timer_interrupt.S diff --git a/common/inc/tx_api.h b/common/inc/tx_api.h index d91bfc9f..40ef7ef5 100644 --- a/common/inc/tx_api.h +++ b/common/inc/tx_api.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* tx_api.h PORTABLE C */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -78,6 +78,10 @@ /* 10-15-2021 Yuxin Zhou Modified comment(s), */ /* update patch number, */ /* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), */ +/* add unused parameter macro, */ +/* update patch number, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -95,6 +99,10 @@ extern "C" { #endif +/* Disable warning of parameter not used. */ +#ifndef TX_PARAMETER_NOT_USED +#define TX_PARAMETER_NOT_USED(p) ((void)(p)) +#endif /* TX_PARAMETER_NOT_USED */ /* Include the port-specific data type file. */ @@ -110,7 +118,7 @@ extern "C" { #define AZURE_RTOS_THREADX #define THREADX_MAJOR_VERSION 6 #define THREADX_MINOR_VERSION 1 -#define THREADX_PATCH_VERSION 9 +#define THREADX_PATCH_VERSION 10 /* Define the following symbol for backward compatibility */ #define EL_PRODUCT_THREADX diff --git a/common_modules/inc/txm_module.h b/common_modules/inc/txm_module.h index aa9d2f92..90a35138 100644 --- a/common_modules/inc/txm_module.h +++ b/common_modules/inc/txm_module.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* txm_module.h PORTABLE C */ -/* 6.1.3 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -44,6 +44,9 @@ /* 12-31-2020 Scott Larson Modified comment(s), added */ /* port-specific extension, */ /* resulting in version 6.1.3 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* callback thread prototype, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -64,23 +67,23 @@ #ifdef TXM_MODULE_ENABLE_FILEX #include "txm_module_filex.h" -#endif +#endif #ifdef TXM_MODULE_ENABLE_GUIX #include "txm_module_guix.h" -#endif +#endif #ifdef TXM_MODULE_ENABLE_NETX #include "txm_module_netx.h" -#endif +#endif #ifdef TXM_MODULE_ENABLE_NETXDUO #include "txm_module_netxduo.h" -#endif +#endif #ifdef TXM_MODULE_ENABLE_USBX #include "txm_module_usbx.h" -#endif +#endif #ifdef FX_FILEX_PRESENT @@ -157,7 +160,7 @@ extern "C" { /* Define each module's callback queue depth. This is used to queue up incoming call back requests. */ #ifndef TXM_MODULE_CALLBACKS_QUEUE_DEPTH -#define TXM_MODULE_CALLBACKS_QUEUE_DEPTH 8 /* Number queued callback requests. */ +#define TXM_MODULE_CALLBACKS_QUEUE_DEPTH 8 /* Number queued callback requests. */ #endif @@ -367,7 +370,7 @@ typedef struct TXM_MODULE_PREAMBLE_STRUCT ULONG txm_module_preamble_property_flags; /* Properties Bit Map */ ULONG txm_module_preamble_shell_entry_function; /* Module shell Entry Function */ ULONG txm_module_preamble_start_function; /* Module Thread Start Function */ - ULONG txm_module_preamble_stop_function; /* Module Thread Stop Function */ + ULONG txm_module_preamble_stop_function; /* Module Thread Stop Function */ ULONG txm_module_preamble_start_stop_priority; /* Module Start/Stop Thread Priority */ ULONG txm_module_preamble_start_stop_stack_size; /* Module Start/Stop Thread Priority */ ULONG txm_module_preamble_callback_function; /* Module Callback Thread Function */ @@ -448,10 +451,10 @@ typedef struct TXM_MODULE_INSTANCE_STRUCT VOID (*txm_module_instance_start_thread_entry)(ULONG); VOID (*txm_module_instance_stop_thread_entry)(ULONG); VOID (*txm_module_instance_callback_request_thread_entry)(ULONG); - + /* Define the port extention to the module manager structure. */ TXM_MODULE_MANAGER_PORT_EXTENSION - + TX_THREAD txm_module_instance_start_stop_thread; TX_THREAD txm_module_instance_callback_request_thread; TX_QUEUE txm_module_instance_callback_request_queue; @@ -462,7 +465,7 @@ typedef struct TXM_MODULE_INSTANCE_STRUCT ULONG txm_module_instance_callback_priority; ULONG txm_module_instance_application_module_id; UINT txm_module_instance_maximum_priority; - + /* Define the head pointer of the list of objects allocated by the module. */ struct TXM_MODULE_ALLOCATED_OBJECT_STRUCT *txm_module_instance_object_list_head; @@ -470,11 +473,11 @@ typedef struct TXM_MODULE_INSTANCE_STRUCT struct TXM_MODULE_INSTANCE_STRUCT *txm_module_instance_loaded_next, - *txm_module_instance_loaded_previous; + *txm_module_instance_loaded_previous; } TXM_MODULE_INSTANCE; -/* Determine if the thread entry info control block has an extension defined. If not, define the extension to +/* Determine if the thread entry info control block has an extension defined. If not, define the extension to whitespace. */ #ifndef TXM_MODULE_THREAD_ENTRY_INFO_USER_EXTENSION @@ -482,9 +485,9 @@ typedef struct TXM_MODULE_INSTANCE_STRUCT #endif -/* Define the thread entry information structure. This structure is placed on the thread's stack such that the +/* Define the thread entry information structure. This structure is placed on the thread's stack such that the module's _txm_thread_shell_entry function does not need to access anything in the thread control block. */ - + typedef struct TXM_MODULE_THREAD_ENTRY_INFO_STRUCT { TX_THREAD *txm_module_thread_entry_info_thread; @@ -516,7 +519,7 @@ typedef struct TXM_MODULE_ALLOCATED_OBJECT_STRUCT } TXM_MODULE_ALLOCATED_OBJECT; -/* Determine if module code is being compiled. If so, remap the ThreadX API to +/* Determine if module code is being compiled. If so, remap the ThreadX API to the module shell functions that will go through the module <-> module manager interface. */ @@ -541,6 +544,7 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN UINT _txm_module_thread_system_suspend(TX_THREAD *thread_ptr); UINT _txm_module_application_request(ULONG request, ALIGN_TYPE param_1, ALIGN_TYPE param_2, ALIGN_TYPE param_3); +VOID _txm_module_callback_request_thread_entry(ULONG id); UINT _txm_module_object_allocate(VOID **object_ptr, ULONG object_size); UINT _txm_module_object_deallocate(VOID *object_ptr); UINT _txm_module_object_pointer_get(UINT object_type, CHAR *name, VOID **object_ptr); @@ -574,7 +578,7 @@ VOID _txm_module_usbx_duo_callback_request(TXM_MODULE_CALLBACK_MESSAGE *callbac /* Map the module manager APIs just in case this is being included from the module manager in the resident portion of the application. */ - + #define txm_module_manager_initialize _txm_module_manager_initialize #define txm_module_manager_absolute_load _txm_module_manager_absolute_load #define txm_module_manager_in_place_load _txm_module_manager_in_place_load @@ -615,7 +619,7 @@ UINT _txm_module_manager_file_load(TXM_MODULE_INSTANCE *module_instance, CHAR * UINT _txm_module_manager_initialize(VOID *module_memory_start, ULONG module_memory_size); UINT _txm_module_manager_absolute_load(TXM_MODULE_INSTANCE *module_instance, CHAR *name, VOID *module_location); UINT _txm_module_manager_in_place_load(TXM_MODULE_INSTANCE *module_instance, CHAR *name, VOID *module_location); -UINT _txm_module_manager_internal_load(TXM_MODULE_INSTANCE *module_instance, CHAR *name, VOID *module_location, +UINT _txm_module_manager_internal_load(TXM_MODULE_INSTANCE *module_instance, CHAR *name, VOID *module_location, ULONG code_size, VOID *code_allocation_ptr, ULONG code_allocation_size); ALIGN_TYPE _txm_module_manager_kernel_dispatch(ULONG kernel_request, ALIGN_TYPE param_0, ALIGN_TYPE param_1, ALIGN_TYPE param_2); UINT _txm_module_manager_object_allocate(VOID **object_ptr_ptr, ULONG object_size, TXM_MODULE_INSTANCE *module_instance); @@ -628,7 +632,7 @@ UINT _txm_module_manager_memory_load(TXM_MODULE_INSTANCE *module_instance, CHAR UINT _txm_module_manager_properties_get(TXM_MODULE_INSTANCE *module_instance, ULONG *module_properties_ptr); UINT _txm_module_manager_start(TXM_MODULE_INSTANCE *module_instance); UINT _txm_module_manager_stop(TXM_MODULE_INSTANCE *module_instance); -UINT _txm_module_manager_thread_create(TX_THREAD *thread_ptr, CHAR *name, VOID (*shell_function)(TX_THREAD *, TXM_MODULE_INSTANCE *), +UINT _txm_module_manager_thread_create(TX_THREAD *thread_ptr, CHAR *name, VOID (*shell_function)(TX_THREAD *, TXM_MODULE_INSTANCE *), VOID (*entry_function)(ULONG), ULONG entry_input, VOID *stack_start, ULONG stack_size, UINT priority, UINT preempt_threshold, ULONG time_slice, UINT auto_start, UINT thread_control_block_size, TXM_MODULE_INSTANCE *module_instance); diff --git a/common_modules/inc/txm_module_user_sample.h b/common_modules/inc/txm_module_user_sample.h index 3587eea3..55b471a7 100644 --- a/common_modules/inc/txm_module_user_sample.h +++ b/common_modules/inc/txm_module_user_sample.h @@ -10,41 +10,44 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** User Specific */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** User Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ -/**************************************************************************/ -/* */ -/* APPLICATION INTERFACE DEFINITION RELEASE */ -/* */ -/* txm_module_user.h PORTABLE C */ -/* 6.1 */ +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* txm_module_user.h PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This file contains user defines for configuring the Module Manager */ -/* in specific ways. This file will have an effect only if the Module */ -/* Manager library is built with TXM_MODULE_INCLUDE_USER_DEFINE_FILE */ -/* defined. Note that all the defines in this file may also be made on */ -/* the command line when building Modules library and application */ -/* objects. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This file contains user defines for configuring the Module Manager */ +/* in specific ways. This file will have an effect only if the Module */ +/* Manager library is built with TXM_MODULE_INCLUDE_USER_DEFINE_FILE */ +/* defined. Note that all the defines in this file may also be made on */ +/* the command line when building Modules library and application */ +/* objects. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED defines, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -57,4 +60,106 @@ /* #define TXM_MODULE_KERNEL_STACK_SIZE 2048 */ + +/* Uncomment any of these defines to prevent modules from being able to make that system call. */ + +/* #define TXM_BLOCK_ALLOCATE_CALL_NOT_USED */ +/* #define TXM_BLOCK_POOL_CREATE_CALL_NOT_USED */ +/* #define TXM_BLOCK_POOL_DELETE_CALL_NOT_USED */ +/* #define TXM_BLOCK_POOL_INFO_GET_CALL_NOT_USED */ +/* #define TXM_BLOCK_POOL_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_BLOCK_POOL_PRIORITIZE_CALL_NOT_USED */ +/* #define TXM_BLOCK_RELEASE_CALL_NOT_USED */ +/* #define TXM_BYTE_ALLOCATE_CALL_NOT_USED */ +/* #define TXM_BYTE_POOL_CREATE_CALL_NOT_USED */ +/* #define TXM_BYTE_POOL_DELETE_CALL_NOT_USED */ +/* #define TXM_BYTE_POOL_INFO_GET_CALL_NOT_USED */ +/* #define TXM_BYTE_POOL_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_BYTE_POOL_PRIORITIZE_CALL_NOT_USED */ +/* #define TXM_BYTE_RELEASE_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_CREATE_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_DELETE_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_GET_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_INFO_GET_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_SET_CALL_NOT_USED */ +/* #define TXM_EVENT_FLAGS_SET_NOTIFY_CALL_NOT_USED */ +/* #define TXM_MUTEX_CREATE_CALL_NOT_USED */ +/* #define TXM_MUTEX_DELETE_CALL_NOT_USED */ +/* #define TXM_MUTEX_GET_CALL_NOT_USED */ +/* #define TXM_MUTEX_INFO_GET_CALL_NOT_USED */ +/* #define TXM_MUTEX_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_MUTEX_PRIORITIZE_CALL_NOT_USED */ +/* #define TXM_MUTEX_PUT_CALL_NOT_USED */ +/* #define TXM_QUEUE_CREATE_CALL_NOT_USED */ +/* #define TXM_QUEUE_DELETE_CALL_NOT_USED */ +/* #define TXM_QUEUE_FLUSH_CALL_NOT_USED */ +/* #define TXM_QUEUE_FRONT_SEND_CALL_NOT_USED */ +/* #define TXM_QUEUE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_QUEUE_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_QUEUE_PRIORITIZE_CALL_NOT_USED */ +/* #define TXM_QUEUE_RECEIVE_CALL_NOT_USED */ +/* #define TXM_QUEUE_SEND_CALL_NOT_USED */ +/* #define TXM_QUEUE_SEND_NOTIFY_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_CEILING_PUT_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_CREATE_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_DELETE_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_GET_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_PRIORITIZE_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_PUT_CALL_NOT_USED */ +/* #define TXM_SEMAPHORE_PUT_NOTIFY_CALL_NOT_USED */ +/* #define TXM_THREAD_CREATE_CALL_NOT_USED */ +/* #define TXM_THREAD_DELETE_CALL_NOT_USED */ +/* #define TXM_THREAD_ENTRY_EXIT_NOTIFY_CALL_NOT_USED */ +/* #define TXM_THREAD_IDENTIFY_CALL_NOT_USED */ +/* #define TXM_THREAD_INFO_GET_CALL_NOT_USED */ +/* #define TXM_THREAD_INTERRUPT_CONTROL_CALL_NOT_USED */ +/* #define TXM_THREAD_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_THREAD_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_THREAD_PREEMPTION_CHANGE_CALL_NOT_USED */ +/* #define TXM_THREAD_PRIORITY_CHANGE_CALL_NOT_USED */ +/* #define TXM_THREAD_RELINQUISH_CALL_NOT_USED */ +/* #define TXM_THREAD_RESET_CALL_NOT_USED */ +/* #define TXM_THREAD_RESUME_CALL_NOT_USED */ +/* #define TXM_THREAD_SLEEP_CALL_NOT_USED */ +/* #define TXM_THREAD_STACK_ERROR_NOTIFY_CALL_NOT_USED */ +/* #define TXM_THREAD_SUSPEND_CALL_NOT_USED */ +/* thread system suspend is needed in _txm_module_thread_shell_entry */ +/* #define TXM_THREAD_TERMINATE_CALL_NOT_USED */ +/* #define TXM_THREAD_TIME_SLICE_CHANGE_CALL_NOT_USED */ +/* #define TXM_THREAD_WAIT_ABORT_CALL_NOT_USED */ +/* #define TXM_TIME_GET_CALL_NOT_USED */ +/* #define TXM_TIME_SET_CALL_NOT_USED */ +/* #define TXM_TIMER_ACTIVATE_CALL_NOT_USED */ +/* #define TXM_TIMER_CHANGE_CALL_NOT_USED */ +/* #define TXM_TIMER_CREATE_CALL_NOT_USED */ +/* #define TXM_TIMER_DEACTIVATE_CALL_NOT_USED */ +/* #define TXM_TIMER_DELETE_CALL_NOT_USED */ +/* #define TXM_TIMER_INFO_GET_CALL_NOT_USED */ +/* #define TXM_TIMER_PERFORMANCE_INFO_GET_CALL_NOT_USED */ +/* #define TXM_TIMER_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED */ +/* #define TXM_TRACE_BUFFER_FULL_NOTIFY_CALL_NOT_USED */ +/* #define TXM_TRACE_DISABLE_CALL_NOT_USED */ +/* #define TXM_TRACE_ENABLE_CALL_NOT_USED */ +/* #define TXM_TRACE_EVENT_FILTER_CALL_NOT_USED */ +/* #define TXM_TRACE_EVENT_UNFILTER_CALL_NOT_USED */ +/* #define TXM_TRACE_INTERRUPT_CONTROL_CALL_NOT_USED */ +/* #define TXM_TRACE_ISR_ENTER_INSERT_CALL_NOT_USED */ +/* #define TXM_TRACE_ISR_EXIT_INSERT_CALL_NOT_USED */ +/* #define TXM_TRACE_USER_EVENT_INSERT_CALL_NOT_USED */ +/* #define TXM_MODULE_APPLICATION_REQUEST_CALL_NOT_USED */ +/* #define TXM_MODULE_OBJECT_ALLOCATE_CALL_NOT_USED */ +/* #define TXM_MODULE_OBJECT_DEALLOCATE_CALL_NOT_USED */ +/* #define TXM_MODULE_OBJECT_POINTER_GET_CALL_NOT_USED */ +/* #define TXM_MODULE_OBJECT_POINTER_GET_EXTENDED_CALL_NOT_USED */ + + #endif diff --git a/common_modules/module_lib/src/txm_block_allocate.c b/common_modules/module_lib/src/txm_block_allocate.c index c3a8d92a..3a96d8eb 100644 --- a/common_modules/module_lib/src/txm_block_allocate.c +++ b/common_modules/module_lib/src/txm_block_allocate.c @@ -22,49 +22,52 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_block_allocate PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_ALLOCATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_block_allocate PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the allocate block memory */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* block_ptr Pointer to place allocated block */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the allocate block memory */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* block_ptr Pointer to place allocated block */ /* pointer */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid pool pointer */ -/* TX_PTR_ERROR Invalid destination pointer */ -/* TX_WAIT_ERROR Invalid wait option */ -/* status Actual Completion status */ -/* */ -/* CALLS */ -/* */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid pool pointer */ +/* TX_PTR_ERROR Invalid destination pointer */ +/* TX_WAIT_ERROR Invalid wait option */ +/* status Actual Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_block_allocate(TX_BLOCK_POOL *pool_ptr, VOID **block_ptr, ULONG wait_option) @@ -78,3 +81,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_pool_create.c b/common_modules/module_lib/src/txm_block_pool_create.c index 5b550aaa..e1982e14 100644 --- a/common_modules/module_lib/src/txm_block_pool_create.c +++ b/common_modules/module_lib/src/txm_block_pool_create.c @@ -22,52 +22,55 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_block_pool_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_POOL_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_block_pool_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the create block memory pool */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* name_ptr Pointer to block pool name */ -/* block_size Number of bytes in each block */ -/* pool_start Address of beginning of pool area */ -/* pool_size Number of bytes in the block pool */ -/* pool_control_block_size Size of block pool control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid pool pointer */ -/* TX_PTR_ERROR Invalid starting address */ -/* TX_SIZE_ERROR Invalid pool size */ -/* TX_CALLER_ERROR Invalid caller of pool */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the create block memory pool */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* name_ptr Pointer to block pool name */ +/* block_size Number of bytes in each block */ +/* pool_start Address of beginning of pool area */ +/* pool_size Number of bytes in the block pool */ +/* pool_control_block_size Size of block pool control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid pool pointer */ +/* TX_PTR_ERROR Invalid starting address */ +/* TX_SIZE_ERROR Invalid pool size */ +/* TX_CALLER_ERROR Invalid caller of pool */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_block_pool_create(TX_BLOCK_POOL *pool_ptr, CHAR *name_ptr, ULONG block_size, VOID *pool_start, ULONG pool_size, UINT pool_control_block_size) @@ -87,3 +90,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_pool_delete.c b/common_modules/module_lib/src/txm_block_pool_delete.c index 89b1d064..9f803296 100644 --- a/common_modules/module_lib/src/txm_block_pool_delete.c +++ b/common_modules/module_lib/src/txm_block_pool_delete.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_block_pool_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_POOL_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_block_pool_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the delete block pool memory */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid memory block pool pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual delete function status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the delete block pool memory */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid memory block pool pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual delete function status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_block_pool_delete(TX_BLOCK_POOL *pool_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_pool_info_get.c b/common_modules/module_lib/src/txm_block_pool_info_get.c index f5a221b2..2cc52dbe 100644 --- a/common_modules/module_lib/src/txm_block_pool_info_get.c +++ b/common_modules/module_lib/src/txm_block_pool_info_get.c @@ -22,52 +22,55 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_block_pool_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_POOL_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_block_pool_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the block pool information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to block pool control blk */ -/* name Destination for the pool name */ -/* available_blocks Number of free blocks in pool */ -/* total_blocks Total number of blocks in pool */ -/* first_suspended Destination for pointer of first */ -/* thread suspended on block pool */ -/* suspended_count Destination for suspended count */ -/* next_pool Destination for pointer to next */ -/* block pool on the created list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid block pool pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the block pool information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to block pool control blk */ +/* name Destination for the pool name */ +/* available_blocks Number of free blocks in pool */ +/* total_blocks Total number of blocks in pool */ +/* first_suspended Destination for pointer of first */ +/* thread suspended on block pool */ +/* suspended_count Destination for suspended count */ +/* next_pool Destination for pointer to next */ +/* block pool on the created list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid block pool pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_block_pool_info_get(TX_BLOCK_POOL *pool_ptr, CHAR **name, ULONG *available_blocks, ULONG *total_blocks, TX_THREAD **first_suspended, ULONG *suspended_count, TX_BLOCK_POOL **next_pool) @@ -88,3 +91,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_pool_performance_info_get.c b/common_modules/module_lib/src/txm_block_pool_performance_info_get.c index 4d040bb1..b94aff3c 100644 --- a/common_modules/module_lib/src/txm_block_pool_performance_info_get.c +++ b/common_modules/module_lib/src/txm_block_pool_performance_info_get.c @@ -22,51 +22,54 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_block_pool_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_POOL_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_block_pool_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* block pool. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to block pool control blk */ -/* allocates Destination for the number of */ -/* allocations from this pool */ -/* releases Destination for the number of */ -/* blocks released back to pool */ -/* suspensions Destination for number of */ -/* suspensions on this pool */ -/* timeouts Destination for number of timeouts*/ -/* on this pool */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* block pool. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to block pool control blk */ +/* allocates Destination for the number of */ +/* allocations from this pool */ +/* releases Destination for the number of */ +/* blocks released back to pool */ +/* suspensions Destination for number of */ +/* suspensions on this pool */ +/* timeouts Destination for number of timeouts*/ +/* on this pool */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_block_pool_performance_info_get(TX_BLOCK_POOL *pool_ptr, ULONG *allocates, ULONG *releases, ULONG *suspensions, ULONG *timeouts) @@ -85,3 +88,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_pool_performance_system_info_get.c b/common_modules/module_lib/src/txm_block_pool_performance_system_info_get.c index 0603a960..1c0da014 100644 --- a/common_modules/module_lib/src/txm_block_pool_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_block_pool_performance_system_info_get.c @@ -22,49 +22,52 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_block_pool_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_block_pool_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves block pool performance information. */ -/* */ -/* INPUT */ -/* */ -/* allocates Destination for the total number */ -/* of block allocations */ -/* releases Destination for the total number */ -/* of blocks released */ -/* suspensions Destination for the total number */ -/* of suspensions */ -/* timeouts Destination for total number of */ -/* timeouts */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves block pool performance information. */ +/* */ +/* INPUT */ +/* */ +/* allocates Destination for the total number */ +/* of block allocations */ +/* releases Destination for the total number */ +/* of blocks released */ +/* suspensions Destination for the total number */ +/* of suspensions */ +/* timeouts Destination for total number of */ +/* timeouts */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_block_pool_performance_system_info_get(ULONG *allocates, ULONG *releases, ULONG *suspensions, ULONG *timeouts) @@ -82,3 +85,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_pool_prioritize.c b/common_modules/module_lib/src/txm_block_pool_prioritize.c index 67b0b4a2..7799331a 100644 --- a/common_modules/module_lib/src/txm_block_pool_prioritize.c +++ b/common_modules/module_lib/src/txm_block_pool_prioritize.c @@ -22,42 +22,45 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_block_pool_prioritize PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_POOL_PRIORITIZE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_block_pool_prioritize PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the block pool prioritize call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the block pool prioritize call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_block_pool_prioritize(TX_BLOCK_POOL *pool_ptr) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_block_release.c b/common_modules/module_lib/src/txm_block_release.c index 370f8821..446f8d38 100644 --- a/common_modules/module_lib/src/txm_block_release.c +++ b/common_modules/module_lib/src/txm_block_release.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_block_release PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BLOCK_RELEASE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_block_release PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the block release function call. */ -/* */ -/* INPUT */ -/* */ -/* block_ptr Pointer to memory block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_PTR_ERROR Invalid memory block pointer */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the block release function call. */ +/* */ +/* INPUT */ +/* */ +/* block_ptr Pointer to memory block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_PTR_ERROR Invalid memory block pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_block_release(VOID *block_ptr) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_allocate.c b/common_modules/module_lib/src/txm_byte_allocate.c index 193c7868..c1f4d8fe 100644 --- a/common_modules/module_lib/src/txm_byte_allocate.c +++ b/common_modules/module_lib/src/txm_byte_allocate.c @@ -22,51 +22,54 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_byte_allocate PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_ALLOCATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_byte_allocate PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in allocate bytes function call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* memory_ptr Pointer to place allocated bytes */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in allocate bytes function call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* memory_ptr Pointer to place allocated bytes */ /* pointer */ -/* memory_size Number of bytes to allocate */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid memory pool pointer */ -/* TX_PTR_ERROR Invalid destination pointer */ -/* TX_WAIT_ERROR Invalid wait option */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* TX_SIZE_ERROR Invalid size of memory request */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* memory_size Number of bytes to allocate */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid memory pool pointer */ +/* TX_PTR_ERROR Invalid destination pointer */ +/* TX_WAIT_ERROR Invalid wait option */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* TX_SIZE_ERROR Invalid size of memory request */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option) @@ -84,3 +87,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_pool_create.c b/common_modules/module_lib/src/txm_byte_pool_create.c index 170892ad..df5dd027 100644 --- a/common_modules/module_lib/src/txm_byte_pool_create.c +++ b/common_modules/module_lib/src/txm_byte_pool_create.c @@ -22,51 +22,54 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_byte_pool_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_POOL_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_byte_pool_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the create byte pool memory */ -/* function. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* name_ptr Pointer to byte pool name */ -/* pool_start Address of beginning of pool area */ -/* pool_size Number of bytes in the byte pool */ -/* pool_control_block_size Size of byte pool control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid byte pool pointer */ -/* TX_PTR_ERROR Invalid pool starting address */ -/* TX_SIZE_ERROR Invalid pool size */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the create byte pool memory */ +/* function. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* name_ptr Pointer to byte pool name */ +/* pool_start Address of beginning of pool area */ +/* pool_size Number of bytes in the byte pool */ +/* pool_control_block_size Size of byte pool control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid byte pool pointer */ +/* TX_PTR_ERROR Invalid pool starting address */ +/* TX_SIZE_ERROR Invalid pool size */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_byte_pool_create(TX_BYTE_POOL *pool_ptr, CHAR *name_ptr, VOID *pool_start, ULONG pool_size, UINT pool_control_block_size) @@ -85,3 +88,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_pool_delete.c b/common_modules/module_lib/src/txm_byte_pool_delete.c index 3160fa91..1d75816c 100644 --- a/common_modules/module_lib/src/txm_byte_pool_delete.c +++ b/common_modules/module_lib/src/txm_byte_pool_delete.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_byte_pool_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_POOL_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_byte_pool_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the delete byte pool function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid pool pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the delete byte pool function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid pool pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_byte_pool_delete(TX_BYTE_POOL *pool_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_pool_info_get.c b/common_modules/module_lib/src/txm_byte_pool_info_get.c index 29d246c7..240fb6b1 100644 --- a/common_modules/module_lib/src/txm_byte_pool_info_get.c +++ b/common_modules/module_lib/src/txm_byte_pool_info_get.c @@ -22,52 +22,55 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_byte_pool_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_POOL_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_byte_pool_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the byte pool information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to byte pool control block*/ -/* name Destination for the pool name */ -/* available_bytes Number of free bytes in byte pool */ -/* fragments Number of fragments in byte pool */ -/* first_suspended Destination for pointer of first */ -/* thread suspended on byte pool */ -/* suspended_count Destination for suspended count */ -/* next_pool Destination for pointer to next */ -/* byte pool on the created list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_POOL_ERROR Invalid byte pool pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the byte pool information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to byte pool control block*/ +/* name Destination for the pool name */ +/* available_bytes Number of free bytes in byte pool */ +/* fragments Number of fragments in byte pool */ +/* first_suspended Destination for pointer of first */ +/* thread suspended on byte pool */ +/* suspended_count Destination for suspended count */ +/* next_pool Destination for pointer to next */ +/* byte pool on the created list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_POOL_ERROR Invalid byte pool pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_byte_pool_info_get(TX_BYTE_POOL *pool_ptr, CHAR **name, ULONG *available_bytes, ULONG *fragments, TX_THREAD **first_suspended, ULONG *suspended_count, TX_BYTE_POOL **next_pool) @@ -88,3 +91,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_pool_performance_info_get.c b/common_modules/module_lib/src/txm_byte_pool_performance_info_get.c index fde49fdb..9c1d1766 100644 --- a/common_modules/module_lib/src/txm_byte_pool_performance_info_get.c +++ b/common_modules/module_lib/src/txm_byte_pool_performance_info_get.c @@ -22,59 +22,62 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_byte_pool_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_POOL_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_byte_pool_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* byte pool. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to byte pool control block*/ -/* allocates Destination for number of */ -/* allocates on this pool */ -/* releases Destination for number of */ -/* releases on this pool */ -/* fragments_searched Destination for number of */ -/* fragments searched during */ -/* allocation */ -/* merges Destination for number of adjacent*/ -/* free fragments merged */ -/* splits Destination for number of */ -/* fragments split during */ -/* allocation */ -/* suspensions Destination for number of */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* byte pool. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to byte pool control block*/ +/* allocates Destination for number of */ +/* allocates on this pool */ +/* releases Destination for number of */ +/* releases on this pool */ +/* fragments_searched Destination for number of */ +/* fragments searched during */ +/* allocation */ +/* merges Destination for number of adjacent*/ +/* free fragments merged */ +/* splits Destination for number of */ +/* fragments split during */ +/* allocation */ +/* suspensions Destination for number of */ /* suspensions on this pool */ -/* timeouts Destination for number of timeouts*/ -/* on this byte pool */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* timeouts Destination for number of timeouts*/ +/* on this byte pool */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_byte_pool_performance_info_get(TX_BYTE_POOL *pool_ptr, ULONG *allocates, ULONG *releases, ULONG *fragments_searched, ULONG *merges, ULONG *splits, ULONG *suspensions, ULONG *timeouts) @@ -96,3 +99,4 @@ ALIGN_TYPE extra_parameters[6]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_pool_performance_system_info_get.c b/common_modules/module_lib/src/txm_byte_pool_performance_system_info_get.c index 3bceabfa..f1f57df5 100644 --- a/common_modules/module_lib/src/txm_byte_pool_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_byte_pool_performance_system_info_get.c @@ -22,57 +22,60 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_byte_pool_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_byte_pool_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves byte pool performance information. */ -/* */ -/* INPUT */ -/* */ -/* allocates Destination for total number of */ -/* allocates */ -/* releases Destination for total number of */ -/* releases */ -/* fragments_searched Destination for total number of */ -/* fragments searched during */ -/* allocation */ -/* merges Destination for total number of */ -/* adjacent free fragments merged */ -/* splits Destination for total number of */ -/* fragments split during */ -/* allocation */ -/* suspensions Destination for total number of */ +/* DESCRIPTION */ +/* */ +/* This function retrieves byte pool performance information. */ +/* */ +/* INPUT */ +/* */ +/* allocates Destination for total number of */ +/* allocates */ +/* releases Destination for total number of */ +/* releases */ +/* fragments_searched Destination for total number of */ +/* fragments searched during */ +/* allocation */ +/* merges Destination for total number of */ +/* adjacent free fragments merged */ +/* splits Destination for total number of */ +/* fragments split during */ +/* allocation */ +/* suspensions Destination for total number of */ /* suspensions */ -/* timeouts Destination for total number of */ -/* timeouts */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* timeouts Destination for total number of */ +/* timeouts */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_byte_pool_performance_system_info_get(ULONG *allocates, ULONG *releases, ULONG *fragments_searched, ULONG *merges, ULONG *splits, ULONG *suspensions, ULONG *timeouts) @@ -93,3 +96,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_pool_prioritize.c b/common_modules/module_lib/src/txm_byte_pool_prioritize.c index 268a6f2f..a86eb409 100644 --- a/common_modules/module_lib/src/txm_byte_pool_prioritize.c +++ b/common_modules/module_lib/src/txm_byte_pool_prioritize.c @@ -22,42 +22,45 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_byte_pool_prioritize PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_POOL_PRIORITIZE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_byte_pool_prioritize PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the byte pool prioritize call. */ -/* */ -/* INPUT */ -/* */ -/* pool_ptr Pointer to pool control block */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the byte pool prioritize call. */ +/* */ +/* INPUT */ +/* */ +/* pool_ptr Pointer to pool control block */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_byte_pool_prioritize(TX_BYTE_POOL *pool_ptr) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_byte_release.c b/common_modules/module_lib/src/txm_byte_release.c index 2dbb8998..c127f30b 100644 --- a/common_modules/module_lib/src/txm_byte_release.c +++ b/common_modules/module_lib/src/txm_byte_release.c @@ -22,44 +22,47 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_byte_release PORTABLE C */ -/* 6.1 */ +#ifndef TXM_BYTE_RELEASE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_byte_release PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the release byte function call. */ -/* */ -/* INPUT */ -/* */ -/* memory_ptr Pointer to allocated memory */ -/* */ -/* OUTPUT */ -/* */ -/* TX_PTR_ERROR Invalid memory pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the release byte function call. */ +/* */ +/* INPUT */ +/* */ +/* memory_ptr Pointer to allocated memory */ +/* */ +/* OUTPUT */ +/* */ +/* TX_PTR_ERROR Invalid memory pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_byte_release(VOID *memory_ptr) @@ -73,3 +76,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_create.c b/common_modules/module_lib/src/txm_event_flags_create.c index cec86527..a8db0d0d 100644 --- a/common_modules/module_lib/src/txm_event_flags_create.c +++ b/common_modules/module_lib/src/txm_event_flags_create.c @@ -22,48 +22,51 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_event_flags_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_event_flags_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the event flag creation function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to event flags group */ -/* control block */ -/* name_ptr Pointer to event flags name */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the event flag creation function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to event flags group */ +/* control block */ +/* name_ptr Pointer to event flags name */ /* event_control_block_size Size of event flags control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_GROUP_ERROR Invalid event flag group pointer */ -/* TX_CALLER_ERROR Invalid calling function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ +/* TX_GROUP_ERROR Invalid event flag group pointer */ +/* TX_CALLER_ERROR Invalid calling function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_event_flags_create(TX_EVENT_FLAGS_GROUP *group_ptr, CHAR *name_ptr, UINT event_control_block_size) @@ -77,3 +80,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_delete.c b/common_modules/module_lib/src/txm_event_flags_delete.c index e69270c5..ebe544cc 100644 --- a/common_modules/module_lib/src/txm_event_flags_delete.c +++ b/common_modules/module_lib/src/txm_event_flags_delete.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_event_flags_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_event_flags_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the delete event flags group */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to group control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_GROUP_ERROR Invalid event flag group pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the delete event flags group */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to group control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_GROUP_ERROR Invalid event flag group pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_event_flags_delete(TX_EVENT_FLAGS_GROUP *group_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_get.c b/common_modules/module_lib/src/txm_event_flags_get.c index 7006bbe8..646ee58c 100644 --- a/common_modules/module_lib/src/txm_event_flags_get.c +++ b/common_modules/module_lib/src/txm_event_flags_get.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_event_flags_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_event_flags_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the event flags get function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to group control block */ -/* requested_event_flags Event flags requested */ -/* get_option Specifies and/or and clear options*/ -/* actual_flags_ptr Pointer to place the actual flags */ -/* the service retrieved */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_GROUP_ERROR Invalid event flags group pointer */ -/* TX_PTR_ERROR Invalid actual flags pointer */ -/* TX_WAIT_ERROR Invalid wait option */ -/* TX_OPTION_ERROR Invalid get option */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the event flags get function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to group control block */ +/* requested_event_flags Event flags requested */ +/* get_option Specifies and/or and clear options*/ +/* actual_flags_ptr Pointer to place the actual flags */ +/* the service retrieved */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_GROUP_ERROR Invalid event flags group pointer */ +/* TX_PTR_ERROR Invalid actual flags pointer */ +/* TX_WAIT_ERROR Invalid wait option */ +/* TX_OPTION_ERROR Invalid get option */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_event_flags_get(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG requested_flags, UINT get_option, ULONG *actual_flags_ptr, ULONG wait_option) @@ -87,3 +90,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_info_get.c b/common_modules/module_lib/src/txm_event_flags_info_get.c index 9861dab1..3bb8b23d 100644 --- a/common_modules/module_lib/src/txm_event_flags_info_get.c +++ b/common_modules/module_lib/src/txm_event_flags_info_get.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_event_flags_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_event_flags_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the event flag information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to event flag group */ -/* name Destination for the event flags */ -/* group name */ -/* current_flags Current event flags */ -/* first_suspended Destination for pointer of first */ -/* thread suspended on event flags */ -/* suspended_count Destination for suspended count */ -/* next_group Destination for pointer to next */ -/* event flag group on the created */ -/* list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_GROUP_ERROR Invalid event flag group pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the event flag information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to event flag group */ +/* name Destination for the event flags */ +/* group name */ +/* current_flags Current event flags */ +/* first_suspended Destination for pointer of first */ +/* thread suspended on event flags */ +/* suspended_count Destination for suspended count */ +/* next_group Destination for pointer to next */ +/* event flag group on the created */ +/* list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_GROUP_ERROR Invalid event flag group pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_event_flags_info_get(TX_EVENT_FLAGS_GROUP *group_ptr, CHAR **name, ULONG *current_flags, TX_THREAD **first_suspended, ULONG *suspended_count, TX_EVENT_FLAGS_GROUP **next_group) @@ -88,3 +91,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_performance_info_get.c b/common_modules/module_lib/src/txm_event_flags_performance_info_get.c index 43a7cf26..169a98ee 100644 --- a/common_modules/module_lib/src/txm_event_flags_performance_info_get.c +++ b/common_modules/module_lib/src/txm_event_flags_performance_info_get.c @@ -22,52 +22,55 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_event_flags_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_event_flags_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* event flag group. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to event flag group */ -/* sets Destination for the number of */ -/* event flag sets on this group */ -/* gets Destination for the number of */ -/* event flag gets on this group */ -/* suspensions Destination for the number of */ -/* event flag suspensions on this */ -/* group */ -/* timeouts Destination for number of timeouts*/ -/* on this event flag group */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* event flag group. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to event flag group */ +/* sets Destination for the number of */ +/* event flag sets on this group */ +/* gets Destination for the number of */ +/* event flag gets on this group */ +/* suspensions Destination for the number of */ +/* event flag suspensions on this */ +/* group */ +/* timeouts Destination for number of timeouts*/ +/* on this event flag group */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_event_flags_performance_info_get(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG *sets, ULONG *gets, ULONG *suspensions, ULONG *timeouts) @@ -86,3 +89,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_performance_system_info_get.c b/common_modules/module_lib/src/txm_event_flags_performance_system_info_get.c index 387cd262..885cd4a1 100644 --- a/common_modules/module_lib/src/txm_event_flags_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_event_flags_performance_system_info_get.c @@ -22,49 +22,52 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_event_flags_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_event_flags_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves system event flag performance information. */ -/* */ -/* INPUT */ -/* */ -/* sets Destination for total number of */ -/* event flag sets */ -/* gets Destination for total number of */ -/* event flag gets */ -/* suspensions Destination for total number of */ -/* event flag suspensions */ -/* timeouts Destination for total number of */ -/* timeouts */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves system event flag performance information. */ +/* */ +/* INPUT */ +/* */ +/* sets Destination for total number of */ +/* event flag sets */ +/* gets Destination for total number of */ +/* event flag gets */ +/* suspensions Destination for total number of */ +/* event flag suspensions */ +/* timeouts Destination for total number of */ +/* timeouts */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_event_flags_performance_system_info_get(ULONG *sets, ULONG *gets, ULONG *suspensions, ULONG *timeouts) @@ -82,3 +85,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_set.c b/common_modules/module_lib/src/txm_event_flags_set.c index 5d204ec0..a45cf4f2 100644 --- a/common_modules/module_lib/src/txm_event_flags_set.c +++ b/common_modules/module_lib/src/txm_event_flags_set.c @@ -22,48 +22,51 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_event_flags_set PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_SET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_event_flags_set PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the set event flags function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to group control block */ -/* flags_to_set Event flags to set */ -/* set_option Specified either AND or OR */ -/* operation on the event flags */ -/* */ -/* OUTPUT */ -/* */ -/* TX_GROUP_ERROR Invalid event flags group pointer */ -/* TX_OPTION_ERROR Invalid set option */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the set event flags function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to group control block */ +/* flags_to_set Event flags to set */ +/* set_option Specified either AND or OR */ +/* operation on the event flags */ +/* */ +/* OUTPUT */ +/* */ +/* TX_GROUP_ERROR Invalid event flags group pointer */ +/* TX_OPTION_ERROR Invalid set option */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_event_flags_set(TX_EVENT_FLAGS_GROUP *group_ptr, ULONG flags_to_set, UINT set_option) @@ -77,3 +80,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_event_flags_set_notify.c b/common_modules/module_lib/src/txm_event_flags_set_notify.c index f72b92a6..1bc05e36 100644 --- a/common_modules/module_lib/src/txm_event_flags_set_notify.c +++ b/common_modules/module_lib/src/txm_event_flags_set_notify.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_event_flags_set_notify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_EVENT_FLAGS_SET_NOTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_event_flags_set_notify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the event flags set notify */ -/* callback function call. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Pointer to group control block*/ -/* group_put_notify Application callback function */ -/* (TX_NULL disables notify) */ -/* */ -/* OUTPUT */ -/* */ -/* status Service return status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the event flags set notify */ +/* callback function call. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Pointer to group control block*/ +/* group_put_notify Application callback function */ +/* (TX_NULL disables notify) */ +/* */ +/* OUTPUT */ +/* */ +/* status Service return status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_event_flags_set_notify(TX_EVENT_FLAGS_GROUP *group_ptr, VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *)) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_module_application_request.c b/common_modules/module_lib/src/txm_module_application_request.c index b84b8f2c..c96ca715 100644 --- a/common_modules/module_lib/src/txm_module_application_request.c +++ b/common_modules/module_lib/src/txm_module_application_request.c @@ -10,69 +10,72 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* txm_module_application_request PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MODULE_APPLICATION_REQUEST_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* txm_module_application_request PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function sends an application-specific request to the resident */ -/* code. */ -/* */ -/* INPUT */ -/* */ -/* request Request ID (application defined) */ -/* param_1 First parameter */ -/* param_2 Second parameter */ -/* param_3 Third parameter */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function sends an application-specific request to the resident */ +/* code. */ +/* */ +/* INPUT */ +/* */ +/* request Request ID (application defined) */ +/* param_1 First parameter */ +/* param_2 Second parameter */ +/* param_3 Third parameter */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_kernel_call_dispatcher */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT txm_module_application_request(ULONG request, ALIGN_TYPE param_1, ALIGN_TYPE param_2, ALIGN_TYPE param_3) { UINT return_value; - + /* Call module manager dispatcher. */ return_value = (UINT)(_txm_module_kernel_call_dispatcher)(TXM_APPLICATION_REQUEST_ID_BASE+request, param_1, param_2, param_3); - + /* Return value to the caller. */ return(return_value); } - +#endif diff --git a/common_modules/module_lib/src/txm_module_callback_request_thread_entry.c b/common_modules/module_lib/src/txm_module_callback_request_thread_entry.c index bbd51104..1a055d2b 100644 --- a/common_modules/module_lib/src/txm_module_callback_request_thread_entry.c +++ b/common_modules/module_lib/src/txm_module_callback_request_thread_entry.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #ifndef TXM_MODULE #define TXM_MODULE @@ -35,54 +35,56 @@ #include "tx_queue.h" -/* Define the global module entry pointer from the start thread of the module. +/* Define the global module entry pointer from the start thread of the module. This structure contains the pointer to the request queue as well as the pointer to the callback response queue. */ extern TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_callback_request_thread_entry PORTABLE C */ -/* 6.1 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_callback_request_thread_entry PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes all module callback requests, transferred */ -/* by the resident code via the callback queue. When the callback is */ -/* complete, the response is sent back to the resident code to */ -/* acknowledge it. */ -/* */ -/* INPUT */ -/* */ -/* id Module thread ID */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* tx_queue_receive Receive callback request */ -/* */ -/* CALLED BY */ -/* */ -/* Initial thread stack frame */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes all module callback requests, transferred */ +/* by the resident code via the callback queue. When the callback is */ +/* complete, the response is sent back to the resident code to */ +/* acknowledge it. */ +/* */ +/* INPUT */ +/* */ +/* id Module thread ID */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* tx_queue_receive Receive callback request */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -VOID _txm_module_callback_request_thread_entry(ULONG id) +VOID _txm_module_callback_request_thread_entry(ULONG id) { TX_QUEUE *request_queue; @@ -95,6 +97,8 @@ VOID (*queue_send_notify)(TX_QUEUE *); VOID (*thread_entry_exit_notify)(TX_THREAD *, UINT); UINT status; + /* Disable warning of parameter not used. */ + TX_PARAMETER_NOT_USED(id); /* Pickup pointer to the request queue. */ request_queue = _txm_module_entry_info -> txm_module_thread_entry_info_callback_request_queue; @@ -102,7 +106,7 @@ UINT status; /* Loop to process callback messages from the module manager. */ while(1) { - + /* Wait for the callback request for the module. */ status = _txe_queue_receive(request_queue, (VOID *) &callback_message, TX_WAIT_FOREVER); @@ -113,21 +117,21 @@ UINT status; /* This should not happen - get out of the loop. */ break; } - + /* Pickup the activation count in the message. */ activation_count = callback_message.txm_module_callback_message_activation_count; - + /* Loop to call the callback function the correct number of times. */ while (activation_count) { - + /* Decrement the activation count. */ activation_count--; /* Now dispatch the callback function. */ switch (callback_message.txm_module_callback_message_type) { - + case TXM_TIMER_CALLBACK: /* Setup timer callback pointer. */ @@ -147,7 +151,7 @@ UINT status; (events_set_notify)((TX_EVENT_FLAGS_GROUP *) callback_message.txm_module_callback_message_param_1); break; - + case TXM_QUEUE_SEND_CALLBACK: /* Setup queue send callback pointer. */ @@ -175,70 +179,68 @@ UINT status; /* Call thread entry/exit notify callback. */ (thread_entry_exit_notify)((TX_THREAD *) callback_message.txm_module_callback_message_param_1, (UINT) callback_message.txm_module_callback_message_param_2); - + break; default: #ifdef TXM_MODULE_ENABLE_NETX - + /* Determine if there is a NetX callback. */ if ((callback_message.txm_module_callback_message_type >= TXM_NETX_CALLBACKS_START) && (callback_message.txm_module_callback_message_type < TXM_NETX_CALLBACKS_END)) { - + /* Call the NetX module callback function. */ _txm_module_netx_callback_request(&callback_message); } #endif - + #ifdef TXM_MODULE_ENABLE_NETXDUO - + /* Determine if there is a NetX Duo callback. */ if ((callback_message.txm_module_callback_message_type >= TXM_NETXDUO_CALLBACKS_START) && (callback_message.txm_module_callback_message_type < TXM_NETXDUO_CALLBACKS_END)) { - + /* Call the NetX Duo module callback function. */ _txm_module_netxduo_callback_request(&callback_message); } #endif - + #ifdef TXM_MODULE_ENABLE_FILEX - + /* Determine if there is a FileX callback. */ if ((callback_message.txm_module_callback_message_type >= TXM_FILEX_CALLBACKS_START) && (callback_message.txm_module_callback_message_type < TXM_FILEX_CALLBACKS_END)) { - + /* Call the FileX module callback function. */ _txm_module_filex_callback_request(&callback_message); } #endif - + #ifdef TXM_MODULE_ENABLE_GUIX - + /* Determine if there is a GUIX callback. */ if ((callback_message.txm_module_callback_message_type >= TXM_GUIX_CALLBACKS_START) && (callback_message.txm_module_callback_message_type < TXM_GUIX_CALLBACKS_END)) { - + /* Call the GUIX module callback function. */ _txm_module_guix_callback_request(&callback_message); } #endif - + #ifdef TXM_MODULE_ENABLE_USBX - + /* Determine if there is a USBX callback. */ if ((callback_message.txm_module_callback_message_type >= TXM_USBX_CALLBACKS_START) && (callback_message.txm_module_callback_message_type < TXM_USBX_CALLBACKS_END)) { - + /* Call the USBX callback function. */ _txm_module_usbx_callback_request(&callback_message); } #endif - + break; } } } } - - diff --git a/common_modules/module_lib/src/txm_module_object_allocate.c b/common_modules/module_lib/src/txm_module_object_allocate.c index 32757926..0c85ae8f 100644 --- a/common_modules/module_lib/src/txm_module_object_allocate.c +++ b/common_modules/module_lib/src/txm_module_object_allocate.c @@ -22,29 +22,29 @@ #define TXM_MODULE #include "txm_module.h" - +#ifndef TXM_MODULE_OBJECT_ALLOCATE_CALL_NOT_USED /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _txm_module_manager_object_allocate PORTABLE C */ -/* 6.1 */ +/* _txm_module_manager_object_allocate PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function allocates memory for an object from the memory pool */ -/* supplied to txm_module_manager_initialize. */ +/* This function allocates memory for an object from the memory pool */ +/* supplied to txm_module_manager_initialize. */ /* */ /* INPUT */ /* */ -/* object_ptr Destination of object pointer on */ -/* successful allocation */ -/* object_size Size in bytes of the object to be */ -/* allocated */ -/* module_instance The module instance that the */ +/* object_ptr Destination of object pointer on */ +/* successful allocation */ +/* object_size Size in bytes of the object to be */ +/* allocated */ +/* module_instance The module instance that the */ /* object belongs to */ /* */ /* OUTPUT */ @@ -63,7 +63,10 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txm_module_object_allocate(VOID **object_ptr, ULONG object_size) @@ -77,3 +80,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_module_object_deallocate.c b/common_modules/module_lib/src/txm_module_object_deallocate.c index 3936a4d8..b31ac05b 100644 --- a/common_modules/module_lib/src/txm_module_object_deallocate.c +++ b/common_modules/module_lib/src/txm_module_object_deallocate.c @@ -22,24 +22,24 @@ #define TXM_MODULE #include "txm_module.h" - +#ifndef TXM_MODULE_OBJECT_DEALLOCATE_CALL_NOT_USED /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _txm_module_manager_object_deallocate PORTABLE C */ -/* 6.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function deallocates a previously allocated object. */ +/* This function deallocates a previously allocated object. */ /* */ /* INPUT */ /* */ -/* object_ptr Object pointer to deallocate */ +/* object_ptr Object pointer to deallocate */ /* */ /* OUTPUT */ /* */ @@ -57,7 +57,10 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txm_module_object_deallocate(VOID *object_ptr) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_module_object_pointer_get.c b/common_modules/module_lib/src/txm_module_object_pointer_get.c index 45160b09..d19742eb 100644 --- a/common_modules/module_lib/src/txm_module_object_pointer_get.c +++ b/common_modules/module_lib/src/txm_module_object_pointer_get.c @@ -22,13 +22,13 @@ #define TXM_MODULE #include "txm_module.h" - +#ifndef TXM_MODULE_OBJECT_POINTER_GET_CALL_NOT_USED /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _txm_module_manager_object_pointer_get PORTABLE C */ -/* 6.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,7 +73,10 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txm_module_object_pointer_get(UINT object_type, CHAR *name, VOID **object_ptr) @@ -87,3 +90,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_module_object_pointer_get_extended.c b/common_modules/module_lib/src/txm_module_object_pointer_get_extended.c index 350ca3af..9f9e5c96 100644 --- a/common_modules/module_lib/src/txm_module_object_pointer_get_extended.c +++ b/common_modules/module_lib/src/txm_module_object_pointer_get_extended.c @@ -22,13 +22,13 @@ #define TXM_MODULE #include "txm_module.h" - +#ifndef TXM_MODULE_OBJECT_POINTER_GET_EXTENDED_CALL_NOT_USED /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _txm_module_manager_object_pointer_get_extended PORTABLE C */ -/* 6.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -76,7 +76,10 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txm_module_object_pointer_get_extended(UINT object_type, CHAR *name, UINT name_length, VOID **object_ptr) @@ -94,3 +97,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_module_thread_system_suspend.c b/common_modules/module_lib/src/txm_module_thread_system_suspend.c index 8121ca4a..287f4ab9 100644 --- a/common_modules/module_lib/src/txm_module_thread_system_suspend.c +++ b/common_modules/module_lib/src/txm_module_thread_system_suspend.c @@ -22,50 +22,53 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_suspend PORTABLE C */ -/* 6.1 */ -/* */ +#ifndef TXM_THREAD_SYSTEM_SUSPEND_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_suspend PORTABLE C */ +/* 6.1.10 */ +/* */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function suspends the specified thread and changes the thread */ -/* state to the value specified. Note: delayed suspension processing */ +/* DESCRIPTION */ +/* */ +/* This function suspends the specified thread and changes the thread */ +/* state to the value specified. Note: delayed suspension processing */ /* is handled outside of this routine. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to suspend */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to suspend */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_thread_priority_change Thread priority change */ -/* _tx_thread_shell_entry Thread shell function */ -/* _tx_thread_sleep Thread sleep */ -/* _tx_thread_suspend Application thread suspend */ -/* _tx_thread_terminate Thread terminate */ -/* Other ThreadX Components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_priority_change Thread priority change */ +/* _tx_thread_shell_entry Thread shell function */ +/* _tx_thread_sleep Thread sleep */ +/* _tx_thread_suspend Application thread suspend */ +/* _tx_thread_terminate Thread terminate */ +/* Other ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txm_module_thread_system_suspend(TX_THREAD *thread_ptr) @@ -79,3 +82,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_create.c b/common_modules/module_lib/src/txm_mutex_create.c index a3a89d69..f27a8ae0 100644 --- a/common_modules/module_lib/src/txm_mutex_create.c +++ b/common_modules/module_lib/src/txm_mutex_create.c @@ -22,49 +22,52 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_mutex_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_mutex_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the create mutex function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ -/* name_ptr Pointer to mutex name */ -/* inherit Initial mutex count */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the create mutex function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* name_ptr Pointer to mutex name */ +/* inherit Initial mutex count */ /* mutex_control_block_size Size of mutex control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_MUTEX_ERROR Invalid mutex pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* TX_INHERIT_ERROR Invalid inherit option */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ +/* TX_MUTEX_ERROR Invalid mutex pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* TX_INHERIT_ERROR Invalid inherit option */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_mutex_create(TX_MUTEX *mutex_ptr, CHAR *name_ptr, UINT inherit, UINT mutex_control_block_size) @@ -82,3 +85,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_delete.c b/common_modules/module_lib/src/txm_mutex_delete.c index 6b720f3e..9ab9bbd6 100644 --- a/common_modules/module_lib/src/txm_mutex_delete.c +++ b/common_modules/module_lib/src/txm_mutex_delete.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_mutex_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_mutex_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the mutex delete function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_MUTEX_ERROR Invalid mutex pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the mutex delete function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_MUTEX_ERROR Invalid mutex pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_mutex_delete(TX_MUTEX *mutex_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_get.c b/common_modules/module_lib/src/txm_mutex_get.c index 2d7da6ba..039c8e8a 100644 --- a/common_modules/module_lib/src/txm_mutex_get.c +++ b/common_modules/module_lib/src/txm_mutex_get.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_mutex_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_mutex_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the mutex get function call. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_MUTEX_ERROR Invalid mutex pointer */ -/* TX_WAIT_ERROR Invalid wait option */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the mutex get function call. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_MUTEX_ERROR Invalid mutex pointer */ +/* TX_WAIT_ERROR Invalid wait option */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_mutex_get(TX_MUTEX *mutex_ptr, ULONG wait_option) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_info_get.c b/common_modules/module_lib/src/txm_mutex_info_get.c index ea1d1ceb..acc13400 100644 --- a/common_modules/module_lib/src/txm_mutex_info_get.c +++ b/common_modules/module_lib/src/txm_mutex_info_get.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_mutex_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_mutex_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the mutex information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ -/* name Destination for the mutex name */ -/* count Destination for the owner count */ -/* owner Destination for the owner's */ -/* thread control block pointer */ -/* first_suspended Destination for pointer of first */ -/* thread suspended on the mutex */ -/* suspended_count Destination for suspended count */ -/* next_mutex Destination for pointer to next */ -/* mutex on the created list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_MUTEX_ERROR Invalid mutex pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the mutex information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* name Destination for the mutex name */ +/* count Destination for the owner count */ +/* owner Destination for the owner's */ +/* thread control block pointer */ +/* first_suspended Destination for pointer of first */ +/* thread suspended on the mutex */ +/* suspended_count Destination for suspended count */ +/* next_mutex Destination for pointer to next */ +/* mutex on the created list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_MUTEX_ERROR Invalid mutex pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_mutex_info_get(TX_MUTEX *mutex_ptr, CHAR **name, ULONG *count, TX_THREAD **owner, TX_THREAD **first_suspended, ULONG *suspended_count, TX_MUTEX **next_mutex) @@ -89,3 +92,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_performance_info_get.c b/common_modules/module_lib/src/txm_mutex_performance_info_get.c index f6c68e6b..b02e5f6d 100644 --- a/common_modules/module_lib/src/txm_mutex_performance_info_get.c +++ b/common_modules/module_lib/src/txm_mutex_performance_info_get.c @@ -22,55 +22,58 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_mutex_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_mutex_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* mutex. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* mutex. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ /* puts Destination for the number of */ -/* puts on to this mutex */ -/* gets Destination for the number of */ -/* gets on this mutex */ -/* suspensions Destination for the number of */ -/* suspensions on this mutex */ -/* timeouts Destination for number of timeouts*/ -/* on this mutex */ -/* inversions Destination for number of priority*/ -/* inversions on this mutex */ -/* inheritances Destination for number of priority*/ -/* inheritances on this mutex */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* puts on to this mutex */ +/* gets Destination for the number of */ +/* gets on this mutex */ +/* suspensions Destination for the number of */ +/* suspensions on this mutex */ +/* timeouts Destination for number of timeouts*/ +/* on this mutex */ +/* inversions Destination for number of priority*/ +/* inversions on this mutex */ +/* inheritances Destination for number of priority*/ +/* inheritances on this mutex */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_mutex_performance_info_get(TX_MUTEX *mutex_ptr, ULONG *puts, ULONG *gets, ULONG *suspensions, ULONG *timeouts, ULONG *inversions, ULONG *inheritances) @@ -91,3 +94,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_performance_system_info_get.c b/common_modules/module_lib/src/txm_mutex_performance_system_info_get.c index 577b35e8..00a3353b 100644 --- a/common_modules/module_lib/src/txm_mutex_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_mutex_performance_system_info_get.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_mutex_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_mutex_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves system mutex performance information. */ -/* */ -/* INPUT */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves system mutex performance information. */ +/* */ +/* INPUT */ +/* */ /* puts Destination for total number of */ -/* mutex puts */ -/* gets Destination for total number of */ -/* mutex gets */ -/* suspensions Destination for total number of */ -/* mutex suspensions */ -/* timeouts Destination for total number of */ -/* mutex timeouts */ -/* inversions Destination for total number of */ -/* mutex priority inversions */ -/* inheritances Destination for total number of */ -/* mutex priority inheritances */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* mutex puts */ +/* gets Destination for total number of */ +/* mutex gets */ +/* suspensions Destination for total number of */ +/* mutex suspensions */ +/* timeouts Destination for total number of */ +/* mutex timeouts */ +/* inversions Destination for total number of */ +/* mutex priority inversions */ +/* inheritances Destination for total number of */ +/* mutex priority inheritances */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_mutex_performance_system_info_get(ULONG *puts, ULONG *gets, ULONG *suspensions, ULONG *timeouts, ULONG *inversions, ULONG *inheritances) @@ -88,3 +91,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_prioritize.c b/common_modules/module_lib/src/txm_mutex_prioritize.c index a15e8cbb..5a9d6b5c 100644 --- a/common_modules/module_lib/src/txm_mutex_prioritize.c +++ b/common_modules/module_lib/src/txm_mutex_prioritize.c @@ -22,42 +22,45 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_mutex_prioritize PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_PRIORITIZE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_mutex_prioritize PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the mutex prioritize call. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the mutex prioritize call. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_mutex_prioritize(TX_MUTEX *mutex_ptr) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_mutex_put.c b/common_modules/module_lib/src/txm_mutex_put.c index 738d08c6..d2cf47e2 100644 --- a/common_modules/module_lib/src/txm_mutex_put.c +++ b/common_modules/module_lib/src/txm_mutex_put.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_mutex_put PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MUTEX_PUT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_mutex_put PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the mutex put function call. */ -/* */ -/* INPUT */ -/* */ -/* mutex_ptr Pointer to mutex control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_MUTEX_ERROR Invalid mutex pointer */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the mutex put function call. */ +/* */ +/* INPUT */ +/* */ +/* mutex_ptr Pointer to mutex control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_MUTEX_ERROR Invalid mutex pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_mutex_put(TX_MUTEX *mutex_ptr) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_create.c b/common_modules/module_lib/src/txm_queue_create.c index a84e8398..e8aa4444 100644 --- a/common_modules/module_lib/src/txm_queue_create.c +++ b/common_modules/module_lib/src/txm_queue_create.c @@ -22,50 +22,53 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue create function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* name_ptr Pointer to queue name */ -/* message_size Size of each queue message */ -/* queue_start Starting address of the queue area*/ -/* queue_size Number of bytes in the queue */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue create function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* name_ptr Pointer to queue name */ +/* message_size Size of each queue message */ +/* queue_start Starting address of the queue area*/ +/* queue_size Number of bytes in the queue */ /* queue_control_block_size Size of queue control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* TX_PTR_ERROR Invalid starting address of queue */ -/* TX_SIZE_ERROR Invalid message queue size */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* TX_PTR_ERROR Invalid starting address of queue */ +/* TX_SIZE_ERROR Invalid message queue size */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_create(TX_QUEUE *queue_ptr, CHAR *name_ptr, UINT message_size, VOID *queue_start, ULONG queue_size, UINT queue_control_block_size) @@ -85,3 +88,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_delete.c b/common_modules/module_lib/src/txm_queue_delete.c index aae1004e..fccd8d0d 100644 --- a/common_modules/module_lib/src/txm_queue_delete.c +++ b/common_modules/module_lib/src/txm_queue_delete.c @@ -22,44 +22,47 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue delete function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue delete function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_delete(TX_QUEUE *queue_ptr) @@ -73,3 +76,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_flush.c b/common_modules/module_lib/src/txm_queue_flush.c index c660e19f..caac5fcf 100644 --- a/common_modules/module_lib/src/txm_queue_flush.c +++ b/common_modules/module_lib/src/txm_queue_flush.c @@ -22,44 +22,47 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_flush PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_FLUSH_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_flush PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue flush function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue flush function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_flush(TX_QUEUE *queue_ptr) @@ -73,3 +76,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_front_send.c b/common_modules/module_lib/src/txm_queue_front_send.c index 47232930..2ba07744 100644 --- a/common_modules/module_lib/src/txm_queue_front_send.c +++ b/common_modules/module_lib/src/txm_queue_front_send.c @@ -22,47 +22,50 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_front_send PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_FRONT_SEND_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_front_send PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue send function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* source_ptr Pointer to message source */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* TX_PTR_ERROR Invalid source pointer - NULL */ -/* TX_WAIT_ERROR Invalid wait option - non thread */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue send function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* source_ptr Pointer to message source */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* TX_PTR_ERROR Invalid source pointer - NULL */ +/* TX_WAIT_ERROR Invalid wait option - non thread */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_front_send(TX_QUEUE *queue_ptr, VOID *source_ptr, ULONG wait_option) @@ -76,3 +79,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_info_get.c b/common_modules/module_lib/src/txm_queue_info_get.c index 045dd26d..3fae1cbb 100644 --- a/common_modules/module_lib/src/txm_queue_info_get.c +++ b/common_modules/module_lib/src/txm_queue_info_get.c @@ -22,52 +22,55 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* name Destination for the queue name */ -/* enqueued Destination for enqueued count */ -/* available_storage Destination for available storage */ -/* first_suspended Destination for pointer of first */ -/* thread suspended on this queue */ -/* suspended_count Destination for suspended count */ -/* next_queue Destination for pointer to next */ -/* queue on the created list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* name Destination for the queue name */ +/* enqueued Destination for enqueued count */ +/* available_storage Destination for available storage */ +/* first_suspended Destination for pointer of first */ +/* thread suspended on this queue */ +/* suspended_count Destination for suspended count */ +/* next_queue Destination for pointer to next */ +/* queue on the created list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_info_get(TX_QUEUE *queue_ptr, CHAR **name, ULONG *enqueued, ULONG *available_storage, TX_THREAD **first_suspended, ULONG *suspended_count, TX_QUEUE **next_queue) @@ -88,3 +91,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_performance_info_get.c b/common_modules/module_lib/src/txm_queue_performance_info_get.c index d68998e9..f3840cc8 100644 --- a/common_modules/module_lib/src/txm_queue_performance_info_get.c +++ b/common_modules/module_lib/src/txm_queue_performance_info_get.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_queue_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_queue_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* queue. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* messages_sent Destination for messages sent */ -/* messages_received Destination for messages received */ -/* empty_suspensions Destination for number of empty */ -/* queue suspensions */ -/* full_suspensions Destination for number of full */ -/* queue suspensions */ -/* full_errors Destination for queue full errors */ -/* returned - no suspension */ -/* timeouts Destination for number of timeouts*/ -/* on this queue */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* queue. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* messages_sent Destination for messages sent */ +/* messages_received Destination for messages received */ +/* empty_suspensions Destination for number of empty */ +/* queue suspensions */ +/* full_suspensions Destination for number of full */ +/* queue suspensions */ +/* full_errors Destination for queue full errors */ +/* returned - no suspension */ +/* timeouts Destination for number of timeouts*/ +/* on this queue */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_queue_performance_info_get(TX_QUEUE *queue_ptr, ULONG *messages_sent, ULONG *messages_received, ULONG *empty_suspensions, ULONG *full_suspensions, ULONG *full_errors, ULONG *timeouts) @@ -89,3 +92,4 @@ ALIGN_TYPE extra_parameters[5]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_performance_system_info_get.c b/common_modules/module_lib/src/txm_queue_performance_system_info_get.c index 8ef7459f..18b33e39 100644 --- a/common_modules/module_lib/src/txm_queue_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_queue_performance_system_info_get.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_queue_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_queue_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves queue system performance information. */ -/* */ -/* INPUT */ -/* */ -/* messages_sent Destination for total messages */ -/* sent */ -/* messages_received Destination for total messages */ -/* received */ -/* empty_suspensions Destination for total empty */ -/* queue suspensions */ -/* full_suspensions Destination for total full */ -/* queue suspensions */ -/* full_errors Destination for total queue full */ -/* errors returned - no suspension */ -/* timeouts Destination for total number of */ -/* timeouts */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves queue system performance information. */ +/* */ +/* INPUT */ +/* */ +/* messages_sent Destination for total messages */ +/* sent */ +/* messages_received Destination for total messages */ +/* received */ +/* empty_suspensions Destination for total empty */ +/* queue suspensions */ +/* full_suspensions Destination for total full */ +/* queue suspensions */ +/* full_errors Destination for total queue full */ +/* errors returned - no suspension */ +/* timeouts Destination for total number of */ +/* timeouts */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_queue_performance_system_info_get(ULONG *messages_sent, ULONG *messages_received, ULONG *empty_suspensions, ULONG *full_suspensions, ULONG *full_errors, ULONG *timeouts) @@ -88,3 +91,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_prioritize.c b/common_modules/module_lib/src/txm_queue_prioritize.c index bd2c1d1d..3c08d3e7 100644 --- a/common_modules/module_lib/src/txm_queue_prioritize.c +++ b/common_modules/module_lib/src/txm_queue_prioritize.c @@ -22,42 +22,45 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_prioritize PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_PRIORITIZE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_prioritize PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue prioritize call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue prioritize call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_prioritize(TX_QUEUE *queue_ptr) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_receive.c b/common_modules/module_lib/src/txm_queue_receive.c index eaa4fa77..a18b34db 100644 --- a/common_modules/module_lib/src/txm_queue_receive.c +++ b/common_modules/module_lib/src/txm_queue_receive.c @@ -22,49 +22,52 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_receive PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_RECEIVE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_receive PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue receive function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* destination_ptr Pointer to message destination */ -/* **** MUST BE LARGE ENOUGH TO */ -/* HOLD MESSAGE **** */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* TX_PTR_ERROR Invalid destination pointer (NULL)*/ -/* TX_WAIT_ERROR Invalid wait option */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue receive function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* destination_ptr Pointer to message destination */ +/* **** MUST BE LARGE ENOUGH TO */ +/* HOLD MESSAGE **** */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* TX_PTR_ERROR Invalid destination pointer (NULL)*/ +/* TX_WAIT_ERROR Invalid wait option */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_receive(TX_QUEUE *queue_ptr, VOID *destination_ptr, ULONG wait_option) @@ -78,3 +81,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_send.c b/common_modules/module_lib/src/txm_queue_send.c index 23f97fc2..9f26f078 100644 --- a/common_modules/module_lib/src/txm_queue_send.c +++ b/common_modules/module_lib/src/txm_queue_send.c @@ -22,47 +22,50 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_send PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_SEND_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_send PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue send function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block */ -/* source_ptr Pointer to message source */ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_QUEUE_ERROR Invalid queue pointer */ -/* TX_PTR_ERROR Invalid source pointer - NULL */ -/* TX_WAIT_ERROR Invalid wait option */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue send function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block */ +/* source_ptr Pointer to message source */ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_QUEUE_ERROR Invalid queue pointer */ +/* TX_PTR_ERROR Invalid source pointer - NULL */ +/* TX_WAIT_ERROR Invalid wait option */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_send(TX_QUEUE *queue_ptr, VOID *source_ptr, ULONG wait_option) @@ -76,3 +79,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_queue_send_notify.c b/common_modules/module_lib/src/txm_queue_send_notify.c index fece5a5a..fb8091a1 100644 --- a/common_modules/module_lib/src/txm_queue_send_notify.c +++ b/common_modules/module_lib/src/txm_queue_send_notify.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_queue_send_notify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_QUEUE_SEND_NOTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_queue_send_notify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the queue send notify */ -/* callback function call. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Pointer to queue control block*/ -/* queue_send_notify Application callback function */ -/* (TX_NULL disables notify) */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the queue send notify */ +/* callback function call. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Pointer to queue control block*/ +/* queue_send_notify Application callback function */ +/* (TX_NULL disables notify) */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_queue_send_notify(TX_QUEUE *queue_ptr, VOID (*queue_send_notify)(TX_QUEUE *notify_queue_ptr)) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_ceiling_put.c b/common_modules/module_lib/src/txm_semaphore_ceiling_put.c index 0b325d72..a596e68d 100644 --- a/common_modules/module_lib/src/txm_semaphore_ceiling_put.c +++ b/common_modules/module_lib/src/txm_semaphore_ceiling_put.c @@ -22,46 +22,49 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_ceiling_put PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_CEILING_PUT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_ceiling_put PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore ceiling put */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore */ -/* ceiling Maximum value of semaphore */ -/* */ -/* OUTPUT */ -/* */ -/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ -/* TX_INVALID_CEILING Invalid semaphore ceiling */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore ceiling put */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore */ +/* ceiling Maximum value of semaphore */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ +/* TX_INVALID_CEILING Invalid semaphore ceiling */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_ceiling_put(TX_SEMAPHORE *semaphore_ptr, ULONG ceiling) @@ -75,3 +78,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_create.c b/common_modules/module_lib/src/txm_semaphore_create.c index c20ac76d..43267e23 100644 --- a/common_modules/module_lib/src/txm_semaphore_create.c +++ b/common_modules/module_lib/src/txm_semaphore_create.c @@ -22,48 +22,51 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the create semaphore function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ -/* name_ptr Pointer to semaphore name */ -/* initial_count Initial semaphore count */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the create semaphore function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ +/* name_ptr Pointer to semaphore name */ +/* initial_count Initial semaphore count */ /* semaphore_control_block_size Size of semaphore control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_create(TX_SEMAPHORE *semaphore_ptr, CHAR *name_ptr, ULONG initial_count, UINT semaphore_control_block_size) @@ -81,3 +84,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_delete.c b/common_modules/module_lib/src/txm_semaphore_delete.c index 1fa5bf5f..1be358a7 100644 --- a/common_modules/module_lib/src/txm_semaphore_delete.c +++ b/common_modules/module_lib/src/txm_semaphore_delete.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore delete function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ -/* */ -/* OUTPUT */ -/* */ -/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore delete function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ +/* */ +/* OUTPUT */ +/* */ +/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_delete(TX_SEMAPHORE *semaphore_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_get.c b/common_modules/module_lib/src/txm_semaphore_get.c index f7ecd3e5..9b1d9d2a 100644 --- a/common_modules/module_lib/src/txm_semaphore_get.c +++ b/common_modules/module_lib/src/txm_semaphore_get.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore get function call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ -/* wait_option Suspension option */ -/* */ -/* OUTPUT */ -/* */ -/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ -/* TX_WAIT_ERROR Invalid wait option */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore get function call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ +/* wait_option Suspension option */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ +/* TX_WAIT_ERROR Invalid wait option */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_get(TX_SEMAPHORE *semaphore_ptr, ULONG wait_option) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_info_get.c b/common_modules/module_lib/src/txm_semaphore_info_get.c index cd2cac80..5a91bb7f 100644 --- a/common_modules/module_lib/src/txm_semaphore_info_get.c +++ b/common_modules/module_lib/src/txm_semaphore_info_get.c @@ -22,52 +22,55 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ -/* name Destination for the semaphore name*/ -/* current_value Destination for current value of */ -/* the semaphore */ -/* first_suspended Destination for pointer of first */ -/* thread suspended on semaphore */ -/* suspended_count Destination for suspended count */ -/* next_semaphore Destination for pointer to next */ -/* semaphore on the created list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ +/* name Destination for the semaphore name*/ +/* current_value Destination for current value of */ +/* the semaphore */ +/* first_suspended Destination for pointer of first */ +/* thread suspended on semaphore */ +/* suspended_count Destination for suspended count */ +/* next_semaphore Destination for pointer to next */ +/* semaphore on the created list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_info_get(TX_SEMAPHORE *semaphore_ptr, CHAR **name, ULONG *current_value, TX_THREAD **first_suspended, ULONG *suspended_count, TX_SEMAPHORE **next_semaphore) @@ -87,3 +90,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_performance_info_get.c b/common_modules/module_lib/src/txm_semaphore_performance_info_get.c index f227f84a..d294eaf7 100644 --- a/common_modules/module_lib/src/txm_semaphore_performance_info_get.c +++ b/common_modules/module_lib/src/txm_semaphore_performance_info_get.c @@ -22,51 +22,54 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_semaphore_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_semaphore_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* semaphore. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* semaphore. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ /* puts Destination for the number of */ -/* puts on to this semaphore */ -/* gets Destination for the number of */ -/* gets on this semaphore */ -/* suspensions Destination for the number of */ -/* suspensions on this semaphore */ -/* timeouts Destination for number of timeouts*/ -/* on this semaphore */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* puts on to this semaphore */ +/* gets Destination for the number of */ +/* gets on this semaphore */ +/* suspensions Destination for the number of */ +/* suspensions on this semaphore */ +/* timeouts Destination for number of timeouts*/ +/* on this semaphore */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_semaphore_performance_info_get(TX_SEMAPHORE *semaphore_ptr, ULONG *puts, ULONG *gets, ULONG *suspensions, ULONG *timeouts) @@ -85,3 +88,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_performance_system_info_get.c b/common_modules/module_lib/src/txm_semaphore_performance_system_info_get.c index 372bab2c..faa2f877 100644 --- a/common_modules/module_lib/src/txm_semaphore_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_semaphore_performance_system_info_get.c @@ -22,49 +22,52 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_semaphore_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_semaphore_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves system semaphore performance information. */ -/* */ -/* INPUT */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves system semaphore performance information. */ +/* */ +/* INPUT */ +/* */ /* puts Destination for total number of */ -/* semaphore puts */ -/* gets Destination for total number of */ -/* semaphore gets */ -/* suspensions Destination for total number of */ -/* semaphore suspensions */ -/* timeouts Destination for total number of */ -/* timeouts */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* semaphore puts */ +/* gets Destination for total number of */ +/* semaphore gets */ +/* suspensions Destination for total number of */ +/* semaphore suspensions */ +/* timeouts Destination for total number of */ +/* timeouts */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_semaphore_performance_system_info_get(ULONG *puts, ULONG *gets, ULONG *suspensions, ULONG *timeouts) @@ -82,3 +85,4 @@ ALIGN_TYPE extra_parameters[2]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_prioritize.c b/common_modules/module_lib/src/txm_semaphore_prioritize.c index 142696aa..c8e40279 100644 --- a/common_modules/module_lib/src/txm_semaphore_prioritize.c +++ b/common_modules/module_lib/src/txm_semaphore_prioritize.c @@ -22,42 +22,45 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_semaphore_prioritize PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_PRIORITIZE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_semaphore_prioritize PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore prioritize call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore prioritize call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_prioritize(TX_SEMAPHORE *semaphore_ptr) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_put.c b/common_modules/module_lib/src/txm_semaphore_put.c index e92c459b..a59712a8 100644 --- a/common_modules/module_lib/src/txm_semaphore_put.c +++ b/common_modules/module_lib/src/txm_semaphore_put.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_put PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_PUT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_put PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore put function call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore control block*/ -/* */ -/* OUTPUT */ -/* */ -/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore put function call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore control block*/ +/* */ +/* OUTPUT */ +/* */ +/* TX_SEMAPHORE_ERROR Invalid semaphore pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_put(TX_SEMAPHORE *semaphore_ptr) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_semaphore_put_notify.c b/common_modules/module_lib/src/txm_semaphore_put_notify.c index a58991e0..5b79df25 100644 --- a/common_modules/module_lib/src/txm_semaphore_put_notify.c +++ b/common_modules/module_lib/src/txm_semaphore_put_notify.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_semaphore_put_notify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_SEMAPHORE_PUT_NOTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_semaphore_put_notify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the semaphore put notify */ -/* callback function call. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Pointer to semaphore */ -/* semaphore_put_notify Application callback function */ -/* (TX_NULL disables notify) */ -/* */ -/* OUTPUT */ -/* */ -/* status Service return status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the semaphore put notify */ +/* callback function call. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Pointer to semaphore */ +/* semaphore_put_notify Application callback function */ +/* (TX_NULL disables notify) */ +/* */ +/* OUTPUT */ +/* */ +/* status Service return status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_semaphore_put_notify(TX_SEMAPHORE *semaphore_ptr, VOID (*semaphore_put_notify)(TX_SEMAPHORE *notify_semaphore_ptr)) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_create.c b/common_modules/module_lib/src/txm_thread_create.c index bd910e6b..e8cb124a 100644 --- a/common_modules/module_lib/src/txm_thread_create.c +++ b/common_modules/module_lib/src/txm_thread_create.c @@ -22,58 +22,61 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread create function call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Thread control block pointer */ -/* name Pointer to thread name string */ -/* entry_function Entry function of the thread */ -/* entry_input 32-bit input value to thread */ -/* stack_start Pointer to start of stack */ -/* stack_size Stack size in bytes */ -/* priority Priority of thread (0-31) */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread create function call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread control block pointer */ +/* name Pointer to thread name string */ +/* entry_function Entry function of the thread */ +/* entry_input 32-bit input value to thread */ +/* stack_start Pointer to start of stack */ +/* stack_size Stack size in bytes */ +/* priority Priority of thread (0-31) */ /* preempt_threshold Preemption threshold */ -/* time_slice Thread time-slice value */ -/* auto_start Automatic start selection */ +/* time_slice Thread time-slice value */ +/* auto_start Automatic start selection */ /* thread_control_block_size Size of thread control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_PTR_ERROR Invalid entry point or stack */ -/* address */ -/* TX_SIZE_ERROR Invalid stack size -too small */ -/* TX_PRIORITY_ERROR Invalid thread priority */ -/* TX_THRESH_ERROR Invalid preemption threshold */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_PTR_ERROR Invalid entry point or stack */ +/* address */ +/* TX_SIZE_ERROR Invalid stack size -too small */ +/* TX_PRIORITY_ERROR Invalid thread priority */ +/* TX_THRESH_ERROR Invalid preemption threshold */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_create(TX_THREAD *thread_ptr, CHAR *name_ptr, VOID (*entry_function)(ULONG entry_input), ULONG entry_input, VOID *stack_start, ULONG stack_size, UINT priority, UINT preempt_threshold, ULONG time_slice, UINT auto_start, UINT thread_control_block_size) @@ -98,3 +101,4 @@ ALIGN_TYPE extra_parameters[9]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_delete.c b/common_modules/module_lib/src/txm_thread_delete.c index 6e1e6b76..8548d243 100644 --- a/common_modules/module_lib/src/txm_thread_delete.c +++ b/common_modules/module_lib/src/txm_thread_delete.c @@ -22,44 +22,47 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread delete function call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to suspend */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread delete function call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to suspend */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_delete(TX_THREAD *thread_ptr) @@ -73,3 +76,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_entry_exit_notify.c b/common_modules/module_lib/src/txm_thread_entry_exit_notify.c index e8fa8b08..ab652457 100644 --- a/common_modules/module_lib/src/txm_thread_entry_exit_notify.c +++ b/common_modules/module_lib/src/txm_thread_entry_exit_notify.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_entry_exit_notify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_ENTRY_EXIT_NOTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_entry_exit_notify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread entry/exit notify */ -/* callback function call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread */ -/* thread_entry_exit_notify Pointer to notify callback */ -/* function, TX_NULL to disable*/ -/* */ -/* OUTPUT */ -/* */ -/* status Service return status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread entry/exit notify */ +/* callback function call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* thread_entry_exit_notify Pointer to notify callback */ +/* function, TX_NULL to disable*/ +/* */ +/* OUTPUT */ +/* */ +/* status Service return status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_entry_exit_notify(TX_THREAD *thread_ptr, VOID (*thread_entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type)) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_identify.c b/common_modules/module_lib/src/txm_thread_identify.c index 7dc4297d..99b14a54 100644 --- a/common_modules/module_lib/src/txm_thread_identify.c +++ b/common_modules/module_lib/src/txm_thread_identify.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_identify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_IDENTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_identify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function returns the control block pointer of the currently */ -/* executing thread. If the return value is NULL, no thread is */ -/* executing. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD * Pointer to control block of */ -/* currently executing thread */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the control block pointer of the currently */ +/* executing thread. If the return value is NULL, no thread is */ +/* executing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD * Pointer to control block of */ +/* currently executing thread */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ TX_THREAD *_tx_thread_identify(VOID) @@ -74,3 +77,4 @@ TX_THREAD *return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_info_get.c b/common_modules/module_lib/src/txm_thread_info_get.c index ca9f4684..4513dca3 100644 --- a/common_modules/module_lib/src/txm_thread_info_get.c +++ b/common_modules/module_lib/src/txm_thread_info_get.c @@ -22,55 +22,58 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread control block */ -/* name Destination for the thread name */ -/* state Destination for thread state */ -/* run_count Destination for thread run count */ -/* priority Destination for thread priority */ -/* preemption_threshold Destination for thread preemption-*/ -/* threshold */ -/* time_slice Destination for thread time-slice */ -/* next_thread Destination for next created */ -/* thread */ -/* next_suspended_thread Destination for next suspended */ -/* thread */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* name Destination for the thread name */ +/* state Destination for thread state */ +/* run_count Destination for thread run count */ +/* priority Destination for thread priority */ +/* preemption_threshold Destination for thread preemption-*/ +/* threshold */ +/* time_slice Destination for thread time-slice */ +/* next_thread Destination for next created */ +/* thread */ +/* next_suspended_thread Destination for next suspended */ +/* thread */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_info_get(TX_THREAD *thread_ptr, CHAR **name, UINT *state, ULONG *run_count, UINT *priority, UINT *preemption_threshold, ULONG *time_slice, TX_THREAD **next_thread, TX_THREAD **next_suspended_thread) @@ -93,3 +96,4 @@ ALIGN_TYPE extra_parameters[7]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_interrupt_control.c b/common_modules/module_lib/src/txm_thread_interrupt_control.c index 628672d3..1352fccc 100644 --- a/common_modules/module_lib/src/txm_thread_interrupt_control.c +++ b/common_modules/module_lib/src/txm_thread_interrupt_control.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_INTERRUPT_CONTROL_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* status | old_posture Return status if feature not */ -/* enabled, old interrupt lockout */ -/* posture if feature enabled. */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* status | old_posture Return status if feature not */ +/* enabled, old interrupt lockout */ +/* posture if feature enabled. */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_thread_interrupt_control(UINT new_posture) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_performance_info_get.c b/common_modules/module_lib/src/txm_thread_performance_info_get.c index c1cac33c..2b2fa4a1 100644 --- a/common_modules/module_lib/src/txm_thread_performance_info_get.c +++ b/common_modules/module_lib/src/txm_thread_performance_info_get.c @@ -22,68 +22,71 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* thread. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread control block */ -/* resumptions Destination for number of times */ -/* thread was resumed */ -/* suspensions Destination for number of times */ -/* thread was suspended */ -/* solicited_preemptions Destination for number of times */ -/* thread called another service */ -/* that resulted in preemption */ -/* interrupt_preemptions Destination for number of times */ -/* thread was preempted by another */ -/* thread made ready in Interrupt */ -/* Service Routine (ISR) */ -/* priority_inversions Destination for number of times */ -/* a priority inversion was */ -/* detected for this thread */ -/* time_slices Destination for number of times */ -/* thread was time-sliced */ -/* relinquishes Destination for number of thread */ -/* relinquishes */ -/* timeouts Destination for number of timeouts*/ -/* for thread */ -/* wait_aborts Destination for number of wait */ -/* aborts for thread */ -/* last_preempted_by Destination for pointer of the */ -/* thread that last preempted this */ -/* thread */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* thread. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* resumptions Destination for number of times */ +/* thread was resumed */ +/* suspensions Destination for number of times */ +/* thread was suspended */ +/* solicited_preemptions Destination for number of times */ +/* thread called another service */ +/* that resulted in preemption */ +/* interrupt_preemptions Destination for number of times */ +/* thread was preempted by another */ +/* thread made ready in Interrupt */ +/* Service Routine (ISR) */ +/* priority_inversions Destination for number of times */ +/* a priority inversion was */ +/* detected for this thread */ +/* time_slices Destination for number of times */ +/* thread was time-sliced */ +/* relinquishes Destination for number of thread */ +/* relinquishes */ +/* timeouts Destination for number of timeouts*/ +/* for thread */ +/* wait_aborts Destination for number of wait */ +/* aborts for thread */ +/* last_preempted_by Destination for pointer of the */ +/* thread that last preempted this */ +/* thread */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_thread_performance_info_get(TX_THREAD *thread_ptr, ULONG *resumptions, ULONG *suspensions, ULONG *solicited_preemptions, ULONG *interrupt_preemptions, ULONG *priority_inversions, ULONG *time_slices, ULONG *relinquishes, ULONG *timeouts, ULONG *wait_aborts, TX_THREAD **last_preempted_by) @@ -108,3 +111,4 @@ ALIGN_TYPE extra_parameters[9]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_performance_system_info_get.c b/common_modules/module_lib/src/txm_thread_performance_system_info_get.c index 5e67e96e..7093a564 100644 --- a/common_modules/module_lib/src/txm_thread_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_thread_performance_system_info_get.c @@ -22,68 +22,71 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves thread system performance information. */ -/* */ -/* INPUT */ -/* */ -/* resumptions Destination for total number of */ -/* thread resumptions */ -/* suspensions Destination for total number of */ -/* thread suspensions */ -/* solicited_preemptions Destination for total number of */ -/* thread preemption from thread */ -/* API calls */ -/* interrupt_preemptions Destination for total number of */ -/* thread preemptions as a result */ -/* of threads made ready inside of */ -/* Interrupt Service Routines */ -/* priority_inversions Destination for total number of */ -/* priority inversions */ -/* time_slices Destination for total number of */ -/* time-slices */ -/* relinquishes Destination for total number of */ -/* relinquishes */ -/* timeouts Destination for total number of */ -/* timeouts */ -/* wait_aborts Destination for total number of */ -/* wait aborts */ -/* non_idle_returns Destination for total number of */ -/* times threads return when */ -/* another thread is ready */ -/* idle_returns Destination for total number of */ -/* times threads return when no */ -/* other thread is ready */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves thread system performance information. */ +/* */ +/* INPUT */ +/* */ +/* resumptions Destination for total number of */ +/* thread resumptions */ +/* suspensions Destination for total number of */ +/* thread suspensions */ +/* solicited_preemptions Destination for total number of */ +/* thread preemption from thread */ +/* API calls */ +/* interrupt_preemptions Destination for total number of */ +/* thread preemptions as a result */ +/* of threads made ready inside of */ +/* Interrupt Service Routines */ +/* priority_inversions Destination for total number of */ +/* priority inversions */ +/* time_slices Destination for total number of */ +/* time-slices */ +/* relinquishes Destination for total number of */ +/* relinquishes */ +/* timeouts Destination for total number of */ +/* timeouts */ +/* wait_aborts Destination for total number of */ +/* wait aborts */ +/* non_idle_returns Destination for total number of */ +/* times threads return when */ +/* another thread is ready */ +/* idle_returns Destination for total number of */ +/* times threads return when no */ +/* other thread is ready */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_thread_performance_system_info_get(ULONG *resumptions, ULONG *suspensions, ULONG *solicited_preemptions, ULONG *interrupt_preemptions, ULONG *priority_inversions, ULONG *time_slices, ULONG *relinquishes, ULONG *timeouts, ULONG *wait_aborts, ULONG *non_idle_returns, ULONG *idle_returns) @@ -108,3 +111,4 @@ ALIGN_TYPE extra_parameters[9]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_preemption_change.c b/common_modules/module_lib/src/txm_thread_preemption_change.c index 9111c15f..5b6a7845 100644 --- a/common_modules/module_lib/src/txm_thread_preemption_change.c +++ b/common_modules/module_lib/src/txm_thread_preemption_change.c @@ -22,48 +22,51 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_preemption_change PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_PREEMPTION_CHANGE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_preemption_change PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the preemption threshold change */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread */ -/* new_threshold New preemption threshold */ -/* old_threshold Old preemption threshold */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_PTR_ERROR Invalid old threshold pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the preemption threshold change */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* new_threshold New preemption threshold */ +/* old_threshold Old preemption threshold */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_PTR_ERROR Invalid old threshold pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_preemption_change(TX_THREAD *thread_ptr, UINT new_threshold, UINT *old_threshold) @@ -77,3 +80,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_priority_change.c b/common_modules/module_lib/src/txm_thread_priority_change.c index 4ee47893..51d2b5f6 100644 --- a/common_modules/module_lib/src/txm_thread_priority_change.c +++ b/common_modules/module_lib/src/txm_thread_priority_change.c @@ -22,48 +22,51 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_priority_change PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_PRIORITY_CHANGE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_priority_change PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the change priority function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to suspend */ -/* new_priority New thread priority */ -/* old_priority Old thread priority */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_PTR_ERROR Invalid old priority pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the change priority function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to suspend */ +/* new_priority New thread priority */ +/* old_priority Old thread priority */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_PTR_ERROR Invalid old priority pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_priority_change(TX_THREAD *thread_ptr, UINT new_priority, UINT *old_priority) @@ -77,3 +80,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_relinquish.c b/common_modules/module_lib/src/txm_thread_relinquish.c index 8e34e235..83d4313d 100644 --- a/common_modules/module_lib/src/txm_thread_relinquish.c +++ b/common_modules/module_lib/src/txm_thread_relinquish.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_relinquish PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_RELINQUISH_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_relinquish PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks to make sure a thread is executing before the */ -/* relinquish is executed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks to make sure a thread is executing before the */ +/* relinquish is executed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _txe_thread_relinquish(VOID) @@ -68,3 +71,4 @@ VOID _txe_thread_relinquish(VOID) /* Call module manager dispatcher. */ (_txm_module_kernel_call_dispatcher)(TXM_THREAD_RELINQUISH_CALL, 0, 0, 0); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_reset.c b/common_modules/module_lib/src/txm_thread_reset.c index 54ea25f7..6cae0bfe 100644 --- a/common_modules/module_lib/src/txm_thread_reset.c +++ b/common_modules/module_lib/src/txm_thread_reset.c @@ -22,44 +22,47 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_reset PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_RESET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_reset PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread reset function call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to reset */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Service return status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread reset function call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to reset */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Service return status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_reset(TX_THREAD *thread_ptr) @@ -73,3 +76,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_resume.c b/common_modules/module_lib/src/txm_thread_resume.c index 0c92b154..9ed41d11 100644 --- a/common_modules/module_lib/src/txm_thread_resume.c +++ b/common_modules/module_lib/src/txm_thread_resume.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_resume PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_RESUME_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_resume PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the resume thread function call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to resume */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the resume thread function call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to resume */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_resume(TX_THREAD *thread_ptr) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_sleep.c b/common_modules/module_lib/src/txm_thread_sleep.c index 2da82b7d..0e33cd43 100644 --- a/common_modules/module_lib/src/txm_thread_sleep.c +++ b/common_modules/module_lib/src/txm_thread_sleep.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_sleep PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_SLEEP_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_sleep PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function handles application thread sleep requests. If the */ -/* sleep request was called from a non-thread, an error is returned. */ -/* */ -/* INPUT */ -/* */ -/* timer_ticks Number of timer ticks to sleep*/ -/* */ -/* OUTPUT */ -/* */ -/* status Return completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function handles application thread sleep requests. If the */ +/* sleep request was called from a non-thread, an error is returned. */ +/* */ +/* INPUT */ +/* */ +/* timer_ticks Number of timer ticks to sleep*/ +/* */ +/* OUTPUT */ +/* */ +/* status Return completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_thread_sleep(ULONG timer_ticks) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_stack_error_notify.c b/common_modules/module_lib/src/txm_thread_stack_error_notify.c index fbad1da7..6946b234 100644 --- a/common_modules/module_lib/src/txm_thread_stack_error_notify.c +++ b/common_modules/module_lib/src/txm_thread_stack_error_notify.c @@ -22,47 +22,50 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_error_notify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_STACK_ERROR_NOTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_error_notify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function registers an application stack error handler. If */ -/* ThreadX detects a stack error, this application handler is called. */ -/* */ -/* Note: stack checking must be enabled for this routine to serve any */ -/* purpose via the TX_ENABLE_STACK_CHECKING define. */ -/* */ -/* INPUT */ -/* */ -/* stack_error_handler Pointer to stack error */ -/* handler, TX_NULL to disable */ -/* */ -/* OUTPUT */ -/* */ -/* status Service return status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function registers an application stack error handler. If */ +/* ThreadX detects a stack error, this application handler is called. */ +/* */ +/* Note: stack checking must be enabled for this routine to serve any */ +/* purpose via the TX_ENABLE_STACK_CHECKING define. */ +/* */ +/* INPUT */ +/* */ +/* stack_error_handler Pointer to stack error */ +/* handler, TX_NULL to disable */ +/* */ +/* OUTPUT */ +/* */ +/* status Service return status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_thread_stack_error_notify(VOID (*stack_error_handler)(TX_THREAD *thread_ptr)) @@ -76,3 +79,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_suspend.c b/common_modules/module_lib/src/txm_thread_suspend.c index a8a25ff1..a88f866c 100644 --- a/common_modules/module_lib/src/txm_thread_suspend.c +++ b/common_modules/module_lib/src/txm_thread_suspend.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_suspend PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_SUSPEND_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_suspend PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread suspend function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to suspend */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread suspend function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to suspend */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_suspend(TX_THREAD *thread_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_terminate.c b/common_modules/module_lib/src/txm_thread_terminate.c index 50acf59f..9947228c 100644 --- a/common_modules/module_lib/src/txm_thread_terminate.c +++ b/common_modules/module_lib/src/txm_thread_terminate.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_terminate PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_TERMINATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_terminate PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread terminate function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to suspend */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread terminate function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to suspend */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_terminate(TX_THREAD *thread_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_time_slice_change.c b/common_modules/module_lib/src/txm_thread_time_slice_change.c index 7183b4c8..29760f28 100644 --- a/common_modules/module_lib/src/txm_thread_time_slice_change.c +++ b/common_modules/module_lib/src/txm_thread_time_slice_change.c @@ -22,47 +22,50 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_time_slice_change PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_TIME_SLICE_CHANGE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_time_slice_change PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the time slice change function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread */ -/* new_time_slice New time slice */ -/* old_time_slice Old time slice */ -/* */ -/* OUTPUT */ -/* */ -/* TX_THREAD_ERROR Invalid thread pointer */ -/* TX_CALLER_ERROR Invalid caller of function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the time slice change function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* new_time_slice New time slice */ +/* old_time_slice Old time slice */ +/* */ +/* OUTPUT */ +/* */ +/* TX_THREAD_ERROR Invalid thread pointer */ +/* TX_CALLER_ERROR Invalid caller of function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_time_slice_change(TX_THREAD *thread_ptr, ULONG new_time_slice, ULONG *old_time_slice) @@ -76,3 +79,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_thread_wait_abort.c b/common_modules/module_lib/src/txm_thread_wait_abort.c index 3177165a..973c7983 100644 --- a/common_modules/module_lib/src/txm_thread_wait_abort.c +++ b/common_modules/module_lib/src/txm_thread_wait_abort.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_thread_wait_abort PORTABLE C */ -/* 6.1 */ +#ifndef TXM_THREAD_WAIT_ABORT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_thread_wait_abort PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the thread wait abort function */ -/* call. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Thread to abort the wait on */ -/* */ -/* OUTPUT */ -/* */ -/* status Return completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the thread wait abort function */ +/* call. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread to abort the wait on */ +/* */ +/* OUTPUT */ +/* */ +/* status Return completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_thread_wait_abort(TX_THREAD *thread_ptr) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_time_get.c b/common_modules/module_lib/src/txm_time_get.c index 0d4751c5..571565e2 100644 --- a/common_modules/module_lib/src/txm_time_get.c +++ b/common_modules/module_lib/src/txm_time_get.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_time_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIME_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_time_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves the internal, free-running, system clock */ -/* and returns it to the caller. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* _tx_timer_system_clock Returns the system clock value */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves the internal, free-running, system clock */ +/* and returns it to the caller. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* _tx_timer_system_clock Returns the system clock value */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ ULONG _tx_time_get(VOID) @@ -72,3 +75,4 @@ ULONG return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_time_set.c b/common_modules/module_lib/src/txm_time_set.c index bb80fcb5..d93786ad 100644 --- a/common_modules/module_lib/src/txm_time_set.c +++ b/common_modules/module_lib/src/txm_time_set.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_time_set PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIME_SET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_time_set PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function modifies the internal, free-running, system clock */ -/* as specified by the caller. */ -/* */ -/* INPUT */ -/* */ -/* new_time New time value */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function modifies the internal, free-running, system clock */ +/* as specified by the caller. */ +/* */ +/* INPUT */ +/* */ +/* new_time New time value */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_time_set(ULONG new_time) @@ -68,3 +71,4 @@ VOID _tx_time_set(ULONG new_time) /* Call module manager dispatcher. */ (_txm_module_kernel_call_dispatcher)(TXM_TIME_SET_CALL, (ALIGN_TYPE) new_time, 0, 0); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_activate.c b/common_modules/module_lib/src/txm_timer_activate.c index 54d09673..22f716fd 100644 --- a/common_modules/module_lib/src/txm_timer_activate.c +++ b/common_modules/module_lib/src/txm_timer_activate.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_timer_activate PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_ACTIVATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_timer_activate PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the activate application timer */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_TIMER_ERROR Invalid application timer */ -/* TX_ACTIVATE_ERROR Application timer already active */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the activate application timer */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TIMER_ERROR Invalid application timer */ +/* TX_ACTIVATE_ERROR Application timer already active */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_timer_activate(TX_TIMER *timer_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_change.c b/common_modules/module_lib/src/txm_timer_change.c index f3bf5c1a..a09162b9 100644 --- a/common_modules/module_lib/src/txm_timer_change.c +++ b/common_modules/module_lib/src/txm_timer_change.c @@ -22,48 +22,51 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_timer_change PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_CHANGE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_timer_change PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the application timer change */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* initial_ticks Initial expiration ticks */ -/* reschedule_ticks Reschedule ticks */ -/* */ -/* OUTPUT */ -/* */ -/* TX_TIMER_ERROR Invalid application timer pointer */ -/* TX_TICK_ERROR Invalid initial tick value of 0 */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the application timer change */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* initial_ticks Initial expiration ticks */ +/* reschedule_ticks Reschedule ticks */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TIMER_ERROR Invalid application timer pointer */ +/* TX_TICK_ERROR Invalid initial tick value of 0 */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_timer_change(TX_TIMER *timer_ptr, ULONG initial_ticks, ULONG reschedule_ticks) @@ -77,3 +80,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_create.c b/common_modules/module_lib/src/txm_timer_create.c index 7e9958fd..d2a1f18f 100644 --- a/common_modules/module_lib/src/txm_timer_create.c +++ b/common_modules/module_lib/src/txm_timer_create.c @@ -22,53 +22,56 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_timer_create PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_CREATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_timer_create PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the create application timer */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* name_ptr Pointer to timer name */ -/* expiration_function Application expiration function */ -/* initial_ticks Initial expiration ticks */ -/* reschedule_ticks Reschedule ticks */ -/* auto_activate Automatic activation flag */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the create application timer */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* name_ptr Pointer to timer name */ +/* expiration_function Application expiration function */ +/* initial_ticks Initial expiration ticks */ +/* reschedule_ticks Reschedule ticks */ +/* auto_activate Automatic activation flag */ /* timer_control_block_size Size of timer control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_TIMER_ERROR Invalid timer control block */ -/* TX_TICK_ERROR Invalid initial expiration count */ -/* TX_ACTIVATE_ERROR Invalid timer activation option */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TIMER_ERROR Invalid timer control block */ +/* TX_TICK_ERROR Invalid initial expiration count */ +/* TX_ACTIVATE_ERROR Invalid timer activation option */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_timer_create(TX_TIMER *timer_ptr, CHAR *name_ptr, VOID (*expiration_function)(ULONG), ULONG expiration_input, ULONG initial_ticks, ULONG reschedule_ticks, UINT auto_activate, UINT timer_control_block_size) @@ -90,3 +93,4 @@ ALIGN_TYPE extra_parameters[6]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_deactivate.c b/common_modules/module_lib/src/txm_timer_deactivate.c index 7ba926a9..e7344b30 100644 --- a/common_modules/module_lib/src/txm_timer_deactivate.c +++ b/common_modules/module_lib/src/txm_timer_deactivate.c @@ -22,44 +22,47 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_timer_deactivate PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_DEACTIVATE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_timer_deactivate PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the deactivate application timer */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_TIMER_ERROR Invalid application timer pointer */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the deactivate application timer */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TIMER_ERROR Invalid application timer pointer */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_timer_deactivate(TX_TIMER *timer_ptr) @@ -73,3 +76,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_delete.c b/common_modules/module_lib/src/txm_timer_delete.c index 2127304f..f27174eb 100644 --- a/common_modules/module_lib/src/txm_timer_delete.c +++ b/common_modules/module_lib/src/txm_timer_delete.c @@ -22,45 +22,48 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_timer_delete PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_DELETE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_timer_delete PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the delete application timer */ -/* function call. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* */ -/* OUTPUT */ -/* */ -/* TX_TIMER_ERROR Invalid application timer pointer */ -/* TX_CALLER_ERROR Invalid caller of this function */ -/* status Actual completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the delete application timer */ +/* function call. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TIMER_ERROR Invalid application timer pointer */ +/* TX_CALLER_ERROR Invalid caller of this function */ +/* status Actual completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_timer_delete(TX_TIMER *timer_ptr) @@ -74,3 +77,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_info_get.c b/common_modules/module_lib/src/txm_timer_info_get.c index f9f2c92d..50279f49 100644 --- a/common_modules/module_lib/src/txm_timer_info_get.c +++ b/common_modules/module_lib/src/txm_timer_info_get.c @@ -22,51 +22,54 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txe_timer_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txe_timer_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks for errors in the timer information get */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* name Destination for the timer name */ -/* active Destination for active flag */ -/* remaining_ticks Destination for remaining ticks */ -/* before expiration */ -/* reschedule_ticks Destination for reschedule ticks */ -/* next_timer Destination for next timer on the */ -/* created list */ -/* */ -/* OUTPUT */ -/* */ -/* TX_TIMER_ERROR Invalid timer pointer */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks for errors in the timer information get */ +/* service. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* name Destination for the timer name */ +/* active Destination for active flag */ +/* remaining_ticks Destination for remaining ticks */ +/* before expiration */ +/* reschedule_ticks Destination for reschedule ticks */ +/* next_timer Destination for next timer on the */ +/* created list */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TIMER_ERROR Invalid timer pointer */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txe_timer_info_get(TX_TIMER *timer_ptr, CHAR **name, UINT *active, ULONG *remaining_ticks, ULONG *reschedule_ticks, TX_TIMER **next_timer) @@ -86,3 +89,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_performance_info_get.c b/common_modules/module_lib/src/txm_timer_performance_info_get.c index b1900704..d2e243b3 100644 --- a/common_modules/module_lib/src/txm_timer_performance_info_get.c +++ b/common_modules/module_lib/src/txm_timer_performance_info_get.c @@ -22,54 +22,57 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_performance_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_PERFORMANCE_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_performance_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves performance information from the specified */ -/* timer. */ -/* */ -/* INPUT */ -/* */ -/* timer_ptr Pointer to timer control block */ -/* activates Destination for the number of */ -/* activations of this timer */ -/* reactivates Destination for the number of */ -/* reactivations of this timer */ -/* deactivates Destination for the number of */ -/* deactivations of this timer */ -/* expirations Destination for the number of */ -/* expirations of this timer */ -/* expiration_adjusts Destination for the number of */ -/* expiration adjustments of this */ -/* timer */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves performance information from the specified */ +/* timer. */ +/* */ +/* INPUT */ +/* */ +/* timer_ptr Pointer to timer control block */ +/* activates Destination for the number of */ +/* activations of this timer */ +/* reactivates Destination for the number of */ +/* reactivations of this timer */ +/* deactivates Destination for the number of */ +/* deactivations of this timer */ +/* expirations Destination for the number of */ +/* expirations of this timer */ +/* expiration_adjusts Destination for the number of */ +/* expiration adjustments of this */ +/* timer */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_timer_performance_info_get(TX_TIMER *timer_ptr, ULONG *activates, ULONG *reactivates, ULONG *deactivates, ULONG *expirations, ULONG *expiration_adjusts) @@ -89,3 +92,4 @@ ALIGN_TYPE extra_parameters[4]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_timer_performance_system_info_get.c b/common_modules/module_lib/src/txm_timer_performance_system_info_get.c index 1a507602..678966fa 100644 --- a/common_modules/module_lib/src/txm_timer_performance_system_info_get.c +++ b/common_modules/module_lib/src/txm_timer_performance_system_info_get.c @@ -22,51 +22,54 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_performance_system_info_get PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TIMER_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_performance_system_info_get PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function retrieves timer performance information. */ -/* */ -/* INPUT */ -/* */ -/* activates Destination for total number of */ -/* activations */ -/* reactivates Destination for total number of */ -/* reactivations */ -/* deactivates Destination for total number of */ -/* deactivations */ -/* expirations Destination for total number of */ -/* expirations */ -/* expiration_adjusts Destination for total number of */ -/* expiration adjustments */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function retrieves timer performance information. */ +/* */ +/* INPUT */ +/* */ +/* activates Destination for total number of */ +/* activations */ +/* reactivates Destination for total number of */ +/* reactivations */ +/* deactivates Destination for total number of */ +/* deactivations */ +/* expirations Destination for total number of */ +/* expirations */ +/* expiration_adjusts Destination for total number of */ +/* expiration adjustments */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_timer_performance_system_info_get(ULONG *activates, ULONG *reactivates, ULONG *deactivates, ULONG *expirations, ULONG *expiration_adjusts) @@ -85,3 +88,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_buffer_full_notify.c b/common_modules/module_lib/src/txm_trace_buffer_full_notify.c index f6641544..56658179 100644 --- a/common_modules/module_lib/src/txm_trace_buffer_full_notify.c +++ b/common_modules/module_lib/src/txm_trace_buffer_full_notify.c @@ -22,46 +22,49 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_buffer_full_notify PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_BUFFER_FULL_NOTIFY_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_buffer_full_notify PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function sets up the application callback function that is */ -/* called whenever the trace buffer becomes full. The application */ -/* can then swap to a new trace buffer in order not to lose any */ -/* events. */ -/* */ -/* INPUT */ -/* */ -/* full_buffer_callback Full trace buffer processing */ -/* function */ -/* */ -/* OUTPUT */ -/* */ -/* Completion Status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function sets up the application callback function that is */ +/* called whenever the trace buffer becomes full. The application */ +/* can then swap to a new trace buffer in order not to lose any */ +/* events. */ +/* */ +/* INPUT */ +/* */ +/* full_buffer_callback Full trace buffer processing */ +/* function */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_buffer_full_notify(VOID (*full_buffer_callback)(VOID *buffer)) @@ -75,3 +78,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_disable.c b/common_modules/module_lib/src/txm_trace_disable.c index b958db9e..d13cfd5a 100644 --- a/common_modules/module_lib/src/txm_trace_disable.c +++ b/common_modules/module_lib/src/txm_trace_disable.c @@ -22,42 +22,45 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_disable PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_DISABLE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_disable PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function disables trace inside of ThreadX. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* Completion Status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function disables trace inside of ThreadX. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_disable(VOID) @@ -71,3 +74,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_enable.c b/common_modules/module_lib/src/txm_trace_enable.c index 1960e0b4..09d96d26 100644 --- a/common_modules/module_lib/src/txm_trace_enable.c +++ b/common_modules/module_lib/src/txm_trace_enable.c @@ -22,46 +22,49 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_enable PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_ENABLE_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_enable PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function initializes the ThreadX trace buffer and the */ -/* associated control variables, enabling it for operation. */ -/* */ -/* INPUT */ -/* */ -/* trace_buffer_start Start of trace buffer */ -/* trace_buffer_size Size (bytes) of trace buffer */ -/* registry_entries Number of object registry */ -/* entries. */ -/* */ -/* OUTPUT */ -/* */ -/* Completion Status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the ThreadX trace buffer and the */ +/* associated control variables, enabling it for operation. */ +/* */ +/* INPUT */ +/* */ +/* trace_buffer_start Start of trace buffer */ +/* trace_buffer_size Size (bytes) of trace buffer */ +/* registry_entries Number of object registry */ +/* entries. */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_enable(VOID *trace_buffer_start, ULONG trace_buffer_size, ULONG registry_entries) @@ -75,3 +78,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_event_filter.c b/common_modules/module_lib/src/txm_trace_event_filter.c index 8a8b4dc9..b1df8557 100644 --- a/common_modules/module_lib/src/txm_trace_event_filter.c +++ b/common_modules/module_lib/src/txm_trace_event_filter.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_event_filter PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_EVENT_FILTER_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_event_filter PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function sets up the event filter, which allows the */ -/* application to filter various trace events during run-time. */ -/* */ -/* INPUT */ -/* */ -/* event_filter_bits Trace filter event bit(s) */ -/* */ -/* OUTPUT */ -/* */ -/* Completion Status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function sets up the event filter, which allows the */ +/* application to filter various trace events during run-time. */ +/* */ +/* INPUT */ +/* */ +/* event_filter_bits Trace filter event bit(s) */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_event_filter(ULONG event_filter_bits) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_event_unfilter.c b/common_modules/module_lib/src/txm_trace_event_unfilter.c index 5afa77a4..e579e5b5 100644 --- a/common_modules/module_lib/src/txm_trace_event_unfilter.c +++ b/common_modules/module_lib/src/txm_trace_event_unfilter.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_event_unfilter PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_EVENT_UNFILTER_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_event_unfilter PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function removes the event filter, which allows the */ -/* application to un-filter various trace events during run-time. */ -/* */ -/* INPUT */ -/* */ -/* event_unfilter_bits Trace un-filter event bit(s) */ -/* */ -/* OUTPUT */ -/* */ -/* Completion Status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function removes the event filter, which allows the */ +/* application to un-filter various trace events during run-time. */ +/* */ +/* INPUT */ +/* */ +/* event_unfilter_bits Trace un-filter event bit(s) */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_event_unfilter(ULONG event_unfilter_bits) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_interrupt_control.c b/common_modules/module_lib/src/txm_trace_interrupt_control.c index 6533ae28..d9bb5de4 100644 --- a/common_modules/module_lib/src/txm_trace_interrupt_control.c +++ b/common_modules/module_lib/src/txm_trace_interrupt_control.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_interrupt_control PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_INTERRUPT_CONTROL_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_interrupt_control PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function provides a shell for the tx_interrupt_control */ -/* function so that a trace event can be logged for its use. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt posture */ -/* */ -/* OUTPUT */ -/* */ -/* Previous Interrupt Posture */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function provides a shell for the tx_interrupt_control */ +/* function so that a trace event can be logged for its use. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Interrupt Posture */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_interrupt_control(UINT new_posture) @@ -72,3 +75,4 @@ UINT return_value; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_isr_enter_insert.c b/common_modules/module_lib/src/txm_trace_isr_enter_insert.c index 730c8c9b..a57a2397 100644 --- a/common_modules/module_lib/src/txm_trace_isr_enter_insert.c +++ b/common_modules/module_lib/src/txm_trace_isr_enter_insert.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_isr_enter_insert PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_ISR_ENTER_INSERT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_isr_enter_insert PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function provides inserts an ISR entry event into the trace */ -/* buffer. */ -/* */ -/* INPUT */ -/* */ -/* isr_id User defined ISR ID */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function provides inserts an ISR entry event into the trace */ +/* buffer. */ +/* */ +/* INPUT */ +/* */ +/* isr_id User defined ISR ID */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_trace_isr_enter_insert(ULONG isr_id) @@ -68,3 +71,4 @@ VOID _tx_trace_isr_enter_insert(ULONG isr_id) /* Call module manager dispatcher. */ (_txm_module_kernel_call_dispatcher)(TXM_TRACE_ISR_ENTER_INSERT_CALL, (ALIGN_TYPE) isr_id, 0, 0); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_isr_exit_insert.c b/common_modules/module_lib/src/txm_trace_isr_exit_insert.c index 078831de..48d0340a 100644 --- a/common_modules/module_lib/src/txm_trace_isr_exit_insert.c +++ b/common_modules/module_lib/src/txm_trace_isr_exit_insert.c @@ -22,43 +22,46 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_isr_exit_insert PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_ISR_EXIT_INSERT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_isr_exit_insert PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function provides inserts an ISR exit event into the trace */ -/* buffer. */ -/* */ -/* INPUT */ -/* */ -/* isr_id User defined ISR ID */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function provides inserts an ISR exit event into the trace */ +/* buffer. */ +/* */ +/* INPUT */ +/* */ +/* isr_id User defined ISR ID */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_trace_isr_exit_insert(ULONG isr_id) @@ -68,3 +71,4 @@ VOID _tx_trace_isr_exit_insert(ULONG isr_id) /* Call module manager dispatcher. */ (_txm_module_kernel_call_dispatcher)(TXM_TRACE_ISR_EXIT_INSERT_CALL, (ALIGN_TYPE) isr_id, 0, 0); } +#endif diff --git a/common_modules/module_lib/src/txm_trace_user_event_insert.c b/common_modules/module_lib/src/txm_trace_user_event_insert.c index 56cc5554..592dab72 100644 --- a/common_modules/module_lib/src/txm_trace_user_event_insert.c +++ b/common_modules/module_lib/src/txm_trace_user_event_insert.c @@ -22,46 +22,49 @@ #define TXM_MODULE #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_trace_user_event_insert PORTABLE C */ -/* 6.1 */ +#ifndef TXM_TRACE_USER_EVENT_INSERT_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_trace_user_event_insert PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function inserts a user-defined event into the trace buffer. */ -/* */ -/* INPUT */ -/* */ -/* event_id User Event ID */ -/* info_field_1 First information field */ -/* info_field_2 First information field */ -/* info_field_3 First information field */ -/* info_field_4 First information field */ -/* */ -/* OUTPUT */ -/* */ -/* Completion Status */ -/* */ -/* CALLS */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user-defined event into the trace buffer. */ +/* */ +/* INPUT */ +/* */ +/* event_id User Event ID */ +/* info_field_1 First information field */ +/* info_field_2 First information field */ +/* info_field_3 First information field */ +/* info_field_4 First information field */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_kernel_call_dispatcher */ -/* */ -/* CALLED BY */ -/* */ -/* Module application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Module application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _tx_trace_user_event_insert(ULONG event_id, ULONG info_field_1, ULONG info_field_2, ULONG info_field_3, ULONG info_field_4) @@ -80,3 +83,4 @@ ALIGN_TYPE extra_parameters[3]; /* Return value to the caller. */ return(return_value); } +#endif diff --git a/common_modules/module_manager/inc/txm_module_manager_dispatch.h b/common_modules/module_manager/inc/txm_module_manager_dispatch.h index 644cc1c9..8cbd2f44 100644 --- a/common_modules/module_manager/inc/txm_module_manager_dispatch.h +++ b/common_modules/module_manager/inc/txm_module_manager_dispatch.h @@ -20,6 +20,7 @@ /**************************************************************************/ /**************************************************************************/ +#ifndef TXM_BLOCK_ALLOCATE_CALL_NOT_USED /* UINT _txe_block_allocate( TX_BLOCK_POOL *pool_ptr, -> param_0 VOID **block_ptr, -> param_1 @@ -46,7 +47,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BLOCK_POOL_CREATE_CALL_NOT_USED /* UINT _txe_block_pool_create( TX_BLOCK_POOL *pool_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -85,7 +88,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BLOCK_POOL_DELETE_CALL_NOT_USED /* UINT _txe_block_pool_delete( TX_BLOCK_POOL *pool_ptr -> param_0 ); */ @@ -111,7 +116,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_BLOCK_POOL_INFO_GET_CALL_NOT_USED /* UINT _txe_block_pool_info_get( TX_BLOCK_POOL *pool_ptr, -> param_0 CHAR **name, -> param_1 @@ -164,7 +171,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BLOCK_POOL_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_block_pool_performance_info_get( TX_BLOCK_POOL *pool_ptr, -> param_0 ULONG *allocates, -> param_1 @@ -207,7 +216,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_block_pool_performance_system_info_get( ULONG *allocates, -> param_0 ULONG *releases, -> param_1 @@ -245,7 +256,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BLOCK_POOL_PRIORITIZE_CALL_NOT_USED /* UINT _txe_block_pool_prioritize( TX_BLOCK_POOL *pool_ptr -> param_0 ); */ @@ -265,7 +278,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BLOCK_RELEASE_CALL_NOT_USED /* UINT _txe_block_release( VOID *block_ptr -> param_0 ); */ @@ -304,7 +319,9 @@ ALIGN_TYPE block_header_start; ); return(return_value); } +#endif +#ifndef TXM_BYTE_ALLOCATE_CALL_NOT_USED /* UINT _txe_byte_allocate( TX_BYTE_POOL *pool_ptr, -> param_0 VOID **memory_ptr, -> param_1 @@ -336,7 +353,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BYTE_POOL_CREATE_CALL_NOT_USED /* UINT _txe_byte_pool_create( TX_BYTE_POOL *pool_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -373,7 +392,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BYTE_POOL_DELETE_CALL_NOT_USED /* UINT _txe_byte_pool_delete( TX_BYTE_POOL *pool_ptr -> param_0 ); */ @@ -399,7 +420,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_BYTE_POOL_INFO_GET_CALL_NOT_USED /* UINT _txe_byte_pool_info_get( TX_BYTE_POOL *pool_ptr, -> param_0 CHAR **name, -> param_1 @@ -452,7 +475,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BYTE_POOL_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_byte_pool_performance_info_get( TX_BYTE_POOL *pool_ptr, -> param_0 ULONG *allocates, -> param_1 @@ -510,7 +535,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_byte_pool_performance_system_info_get( ULONG *allocates, -> param_0 ULONG *releases, -> param_1 @@ -563,7 +590,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BYTE_POOL_PRIORITIZE_CALL_NOT_USED /* UINT _txe_byte_pool_prioritize( TX_BYTE_POOL *pool_ptr -> param_0 ); */ @@ -583,7 +612,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_BYTE_RELEASE_CALL_NOT_USED /* UINT _txe_byte_release( VOID *memory_ptr -> param_0 ); */ @@ -622,7 +653,9 @@ ALIGN_TYPE block_header_start; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_CREATE_CALL_NOT_USED /* UINT _txe_event_flags_create( TX_EVENT_FLAGS_GROUP *group_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -649,7 +682,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_DELETE_CALL_NOT_USED /* UINT _txe_event_flags_delete( TX_EVENT_FLAGS_GROUP *group_ptr -> param_0 ); */ @@ -675,7 +710,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_GET_CALL_NOT_USED /* UINT _txe_event_flags_get( TX_EVENT_FLAGS_GROUP *group_ptr, -> param_0 ULONG requested_flags, -> param_1 @@ -709,7 +746,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_INFO_GET_CALL_NOT_USED /* UINT _txe_event_flags_info_get( TX_EVENT_FLAGS_GROUP *group_ptr, -> param_0 CHAR **name, -> param_1 @@ -757,7 +796,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_event_flags_performance_info_get( TX_EVENT_FLAGS_GROUP *group_ptr, -> param_0 ULONG *sets, -> param_1 @@ -800,7 +841,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_event_flags_performance_system_info_get( ULONG *sets, -> param_0 ULONG *gets, -> param_1 @@ -838,7 +881,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_SET_CALL_NOT_USED /* UINT _txe_event_flags_set( TX_EVENT_FLAGS_GROUP *group_ptr, -> param_0 ULONG flags_to_set, -> param_1 @@ -862,7 +907,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_EVENT_FLAGS_SET_NOTIFY_CALL_NOT_USED /* UINT _txe_event_flags_set_notify( TX_EVENT_FLAGS_GROUP *group_ptr, -> param_0 VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *) -> param_1 @@ -906,7 +953,9 @@ VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *); ); return(return_value); } +#endif +#ifndef TXM_MUTEX_CREATE_CALL_NOT_USED /* UINT _txe_mutex_create( TX_MUTEX *mutex_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -938,7 +987,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MUTEX_DELETE_CALL_NOT_USED /* UINT _txe_mutex_delete( TX_MUTEX *mutex_ptr -> param_0 ); */ @@ -964,7 +1015,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_MUTEX_GET_CALL_NOT_USED /* UINT _txe_mutex_get( TX_MUTEX *mutex_ptr, -> param_0 ULONG wait_option -> param_1 @@ -986,7 +1039,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MUTEX_INFO_GET_CALL_NOT_USED /* UINT _txe_mutex_info_get( TX_MUTEX *mutex_ptr, -> param_0 CHAR **name, -> param_1 @@ -1039,7 +1094,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MUTEX_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_mutex_performance_info_get( TX_MUTEX *mutex_ptr, -> param_0 ULONG *puts, -> param_1 @@ -1092,7 +1149,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_mutex_performance_system_info_get( ULONG *puts, -> param_0 ULONG *gets, -> param_1 @@ -1140,7 +1199,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MUTEX_PRIORITIZE_CALL_NOT_USED /* UINT _txe_mutex_prioritize( TX_MUTEX *mutex_ptr -> param_0 ); */ @@ -1160,7 +1221,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MUTEX_PUT_CALL_NOT_USED /* UINT _txe_mutex_put( TX_MUTEX *mutex_ptr -> param_0 ); */ @@ -1180,7 +1243,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_CREATE_CALL_NOT_USED /* UINT _txe_queue_create( TX_QUEUE *queue_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -1219,7 +1284,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_DELETE_CALL_NOT_USED /* UINT _txe_queue_delete( TX_QUEUE *queue_ptr -> param_0 ); */ @@ -1245,7 +1312,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_QUEUE_FLUSH_CALL_NOT_USED /* UINT _txe_queue_flush( TX_QUEUE *queue_ptr -> param_0 ); */ @@ -1265,7 +1334,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_FRONT_SEND_CALL_NOT_USED /* UINT _txe_queue_front_send( TX_QUEUE *queue_ptr, -> param_0 VOID *source_ptr, -> param_1 @@ -1283,7 +1354,7 @@ TX_QUEUE *queue_ptr; return(TXM_MODULE_INVALID_MEMORY); /* We need to get the size of the message from the queue. */ - queue_ptr = (TX_QUEUE *) param_0; + queue_ptr = (TX_QUEUE *) param_0; if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, param_1, queue_ptr -> tx_queue_message_size)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1295,7 +1366,9 @@ TX_QUEUE *queue_ptr; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_INFO_GET_CALL_NOT_USED /* UINT _txe_queue_info_get( TX_QUEUE *queue_ptr, -> param_0 CHAR **name, -> param_1 @@ -1348,7 +1421,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_queue_performance_info_get( TX_QUEUE *queue_ptr, -> param_0 ULONG *messages_sent, -> param_1 @@ -1401,7 +1476,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_queue_performance_system_info_get( ULONG *messages_sent, -> param_0 ULONG *messages_received, -> param_1 @@ -1449,7 +1526,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_PRIORITIZE_CALL_NOT_USED /* UINT _txe_queue_prioritize( TX_QUEUE *queue_ptr -> param_0 ); */ @@ -1469,7 +1548,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_RECEIVE_CALL_NOT_USED /* UINT _txe_queue_receive( TX_QUEUE *queue_ptr, -> param_0 VOID *destination_ptr, -> param_1 @@ -1487,7 +1568,7 @@ TX_QUEUE *queue_ptr; return(TXM_MODULE_INVALID_MEMORY); /* We need to get the max size of the buffer from the queue. */ - queue_ptr = (TX_QUEUE *) param_0; + queue_ptr = (TX_QUEUE *) param_0; if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG)*queue_ptr -> tx_queue_message_size)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1499,7 +1580,9 @@ TX_QUEUE *queue_ptr; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_SEND_CALL_NOT_USED /* UINT _txe_queue_send( TX_QUEUE *queue_ptr, -> param_0 VOID *source_ptr, -> param_1 @@ -1517,7 +1600,7 @@ TX_QUEUE *queue_ptr; return(TXM_MODULE_INVALID_MEMORY); /* We need to get the size of the message from the queue. */ - queue_ptr = (TX_QUEUE *) param_0; + queue_ptr = (TX_QUEUE *) param_0; if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, param_1, sizeof(ULONG)*queue_ptr -> tx_queue_message_size)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1529,7 +1612,9 @@ TX_QUEUE *queue_ptr; ); return(return_value); } +#endif +#ifndef TXM_QUEUE_SEND_NOTIFY_CALL_NOT_USED /* UINT _txe_queue_send_notify( TX_QUEUE *queue_ptr, -> param_0 VOID (*queue_send_notify)(TX_QUEUE *notify_queue_ptr) -> param_1 @@ -1573,7 +1658,9 @@ VOID (*queue_send_notify)(TX_QUEUE *); ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_CEILING_PUT_CALL_NOT_USED /* UINT _txe_semaphore_ceiling_put( TX_SEMAPHORE *semaphore_ptr, -> param_0 ULONG ceiling -> param_1 @@ -1595,7 +1682,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_CREATE_CALL_NOT_USED /* UINT _txe_semaphore_create( TX_SEMAPHORE *semaphore_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -1627,7 +1716,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_DELETE_CALL_NOT_USED /* UINT _txe_semaphore_delete( TX_SEMAPHORE *semaphore_ptr -> param_0 ); */ @@ -1653,7 +1744,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_GET_CALL_NOT_USED /* UINT _txe_semaphore_get( TX_SEMAPHORE *semaphore_ptr, -> param_0 ULONG wait_option -> param_1 @@ -1675,7 +1768,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_INFO_GET_CALL_NOT_USED /* UINT _txe_semaphore_info_get( TX_SEMAPHORE *semaphore_ptr, -> param_0 CHAR **name, -> param_1 @@ -1723,7 +1818,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_semaphore_performance_info_get( TX_SEMAPHORE *semaphore_ptr, -> param_0 ULONG *puts, -> param_1 @@ -1766,7 +1863,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_semaphore_performance_system_info_get( ULONG *puts, -> param_0 ULONG *gets, -> param_1 @@ -1804,7 +1903,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_PRIORITIZE_CALL_NOT_USED /* UINT _txe_semaphore_prioritize( TX_SEMAPHORE *semaphore_ptr -> param_0 ); */ @@ -1824,7 +1925,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_PUT_CALL_NOT_USED /* UINT _txe_semaphore_put( TX_SEMAPHORE *semaphore_ptr -> param_0 ); */ @@ -1844,7 +1947,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_SEMAPHORE_PUT_NOTIFY_CALL_NOT_USED /* UINT _txe_semaphore_put_notify( TX_SEMAPHORE *semaphore_ptr, -> param_0 VOID (*semaphore_put_notify)(TX_SEMAPHORE *notify_semaphore_ptr) -> param_1 @@ -1888,7 +1993,9 @@ VOID (*semaphore_put_notify)(TX_SEMAPHORE *); ); return(return_value); } +#endif +#ifndef TXM_THREAD_CREATE_CALL_NOT_USED /* UINT _txe_thread_create( TX_THREAD *thread_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -1945,7 +2052,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_DELETE_CALL_NOT_USED /* UINT _txe_thread_delete( TX_THREAD *thread_ptr -> param_0 ); */ @@ -1971,7 +2080,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_THREAD_ENTRY_EXIT_NOTIFY_CALL_NOT_USED /* UINT _txe_thread_entry_exit_notify( TX_THREAD *thread_ptr, -> param_0 VOID (*thread_entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT type) -> param_1 @@ -2020,7 +2131,9 @@ VOID (*thread_entry_exit_notify)(TX_THREAD *, UINT); ); return(return_value); } +#endif +#ifndef TXM_THREAD_IDENTIFY_CALL_NOT_USED /* TX_THREAD *_tx_thread_identify(); */ static ALIGN_TYPE _txm_module_manager_tx_thread_identify_dispatch(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE param_0, ALIGN_TYPE param_1, ALIGN_TYPE *extra_parameters) { @@ -2030,7 +2143,9 @@ ALIGN_TYPE return_value; return_value = (ALIGN_TYPE) _tx_thread_identify(); return(return_value); } +#endif +#ifndef TXM_THREAD_INFO_GET_CALL_NOT_USED /* UINT _txe_thread_info_get( TX_THREAD *thread_ptr, -> param_0 CHAR **name, -> param_1 @@ -2093,7 +2208,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_INTERRUPT_CONTROL_CALL_NOT_USED /* UINT _tx_thread_interrupt_control( UINT new_posture -> param_0 ); */ @@ -2110,7 +2227,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_thread_performance_info_get( TX_THREAD *thread_ptr, -> param_0 ULONG *resumptions, -> param_1 @@ -2183,7 +2302,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_thread_performance_system_info_get( ULONG *resumptions, -> param_0 ULONG *suspensions, -> param_1 @@ -2256,7 +2377,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_PREEMPTION_CHANGE_CALL_NOT_USED /* UINT _txe_thread_preemption_change( TX_THREAD *thread_ptr, -> param_0 UINT new_threshold, -> param_1 @@ -2283,7 +2406,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_PRIORITY_CHANGE_CALL_NOT_USED /* UINT _txe_thread_priority_change( TX_THREAD *thread_ptr, -> param_0 UINT new_priority, -> param_1 @@ -2310,7 +2435,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_RELINQUISH_CALL_NOT_USED /* VOID _txe_thread_relinquish(); */ static ALIGN_TYPE _txm_module_manager_tx_thread_relinquish_dispatch(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE param_0, ALIGN_TYPE param_1, ALIGN_TYPE *extra_parameters) { @@ -2318,7 +2445,9 @@ static ALIGN_TYPE _txm_module_manager_tx_thread_relinquish_dispatch(TXM_MODULE_I _txe_thread_relinquish(); return(TX_SUCCESS); } +#endif +#ifndef TXM_THREAD_RESET_CALL_NOT_USED /* UINT _txe_thread_reset( TX_THREAD *thread_ptr -> param_0 ); */ @@ -2338,7 +2467,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_RESUME_CALL_NOT_USED /* UINT _txe_thread_resume( TX_THREAD *thread_ptr -> param_0 ); */ @@ -2358,7 +2489,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_SLEEP_CALL_NOT_USED /* UINT _tx_thread_sleep( ULONG timer_ticks -> param_0 ); */ @@ -2372,7 +2505,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_STACK_ERROR_NOTIFY_CALL_NOT_USED /* UINT _tx_thread_stack_error_notify( VOID (*stack_error_handler)(TX_THREAD *thread_ptr) -> param_0 ); */ @@ -2389,7 +2524,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_SUSPEND_CALL_NOT_USED /* UINT _txe_thread_suspend( TX_THREAD *thread_ptr -> param_0 ); */ @@ -2409,7 +2546,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_SYSTEM_SUSPEND_CALL_NOT_USED /* VOID _tx_thread_system_suspend( TX_THREAD *thread_ptr -> param_0 ); */ @@ -2464,7 +2603,9 @@ TX_THREAD *thread_ptr; ); return(TX_SUCCESS); } +#endif +#ifndef TXM_THREAD_TERMINATE_CALL_NOT_USED /* UINT _txe_thread_terminate( TX_THREAD *thread_ptr -> param_0 ); */ @@ -2484,7 +2625,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_TIME_SLICE_CHANGE_CALL_NOT_USED /* UINT _txe_thread_time_slice_change( TX_THREAD *thread_ptr, -> param_0 ULONG new_time_slice, -> param_1 @@ -2511,7 +2654,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_THREAD_WAIT_ABORT_CALL_NOT_USED /* UINT _txe_thread_wait_abort( TX_THREAD *thread_ptr -> param_0 ); */ @@ -2531,7 +2676,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TIME_GET_CALL_NOT_USED /* ULONG _tx_time_get(); */ static ALIGN_TYPE _txm_module_manager_tx_time_get_dispatch(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE param_0, ALIGN_TYPE param_1, ALIGN_TYPE *extra_parameters) { @@ -2541,7 +2688,9 @@ ALIGN_TYPE return_value; return_value = (ALIGN_TYPE) _tx_time_get(); return(return_value); } +#endif +#ifndef TXM_TIME_SET_CALL_NOT_USED /* VOID _tx_time_set( ULONG new_time -> param_0 ); */ @@ -2553,7 +2702,9 @@ static ALIGN_TYPE _txm_module_manager_tx_time_set_dispatch(TXM_MODULE_INSTANCE * ); return(TX_SUCCESS); } +#endif +#ifndef TXM_TIMER_ACTIVATE_CALL_NOT_USED /* UINT _txe_timer_activate( TX_TIMER *timer_ptr -> param_0 ); */ @@ -2573,7 +2724,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TIMER_CHANGE_CALL_NOT_USED /* UINT _txe_timer_change( TX_TIMER *timer_ptr, -> param_0 ULONG initial_ticks, -> param_1 @@ -2597,7 +2750,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TIMER_CREATE_CALL_NOT_USED /* UINT _txe_timer_create( TX_TIMER *timer_ptr, -> param_0 CHAR *name_ptr, -> param_1 @@ -2668,7 +2823,9 @@ VOID (*expiration_function)(ULONG); } return(return_value); } +#endif +#ifndef TXM_TIMER_DEACTIVATE_CALL_NOT_USED /* UINT _txe_timer_deactivate( TX_TIMER *timer_ptr -> param_0 ); */ @@ -2688,7 +2845,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TIMER_DELETE_CALL_NOT_USED /* UINT _txe_timer_delete( TX_TIMER *timer_ptr -> param_0 ); */ @@ -2714,7 +2873,9 @@ ALIGN_TYPE return_value; } return(return_value); } +#endif +#ifndef TXM_TIMER_INFO_GET_CALL_NOT_USED /* UINT _txe_timer_info_get( TX_TIMER *timer_ptr, -> param_0 CHAR **name, -> param_1 @@ -2762,7 +2923,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TIMER_PERFORMANCE_INFO_GET_CALL_NOT_USED /* UINT _tx_timer_performance_info_get( TX_TIMER *timer_ptr, -> param_0 ULONG *activates, -> param_1 @@ -2810,7 +2973,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TIMER_PERFORMANCE_SYSTEM_INFO_GET_CALL_NOT_USED /* UINT _tx_timer_performance_system_info_get( ULONG *activates, -> param_0 ULONG *reactivates, -> param_1 @@ -2853,7 +3018,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TRACE_BUFFER_FULL_NOTIFY_CALL_NOT_USED /* UINT _tx_trace_buffer_full_notify( VOID (*full_buffer_callback)(VOID *buffer) -> param_0 ); */ @@ -2867,7 +3034,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TRACE_DISABLE_CALL_NOT_USED /* UINT _tx_trace_disable(); */ static ALIGN_TYPE _txm_module_manager_tx_trace_disable_dispatch(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE param_0, ALIGN_TYPE param_1, ALIGN_TYPE *extra_parameters) { @@ -2880,7 +3049,9 @@ ALIGN_TYPE return_value; return_value = (ALIGN_TYPE) _tx_trace_disable(); return(return_value); } +#endif +#ifndef TXM_TRACE_ENABLE_CALL_NOT_USED /* UINT _tx_trace_enable( VOID *trace_buffer_start, -> param_0 ULONG trace_buffer_size, -> param_1 @@ -2901,7 +3072,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TRACE_EVENT_FILTER_CALL_NOT_USED /* UINT _tx_trace_event_filter( ULONG event_filter_bits -> param_0 ); */ @@ -2915,7 +3088,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TRACE_EVENT_UNFILTER_CALL_NOT_USED /* UINT _tx_trace_event_unfilter( ULONG event_unfilter_bits -> param_0 ); */ @@ -2929,7 +3104,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TRACE_INTERRUPT_CONTROL_CALL_NOT_USED /* UINT _tx_trace_interrupt_control( UINT new_posture -> param_0 ); */ @@ -2946,7 +3123,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_TRACE_ISR_ENTER_INSERT_CALL_NOT_USED /* VOID _tx_trace_isr_enter_insert( ULONG isr_id -> param_0 ); */ @@ -2961,7 +3140,9 @@ static ALIGN_TYPE _txm_module_manager_tx_trace_isr_enter_insert_dispatch(TXM_MOD ); return(TX_SUCCESS); } +#endif +#ifndef TXM_TRACE_ISR_EXIT_INSERT_CALL_NOT_USED /* VOID _tx_trace_isr_exit_insert( ULONG isr_id -> param_0 ); */ @@ -2976,7 +3157,9 @@ static ALIGN_TYPE _txm_module_manager_tx_trace_isr_exit_insert_dispatch(TXM_MODU ); return(TX_SUCCESS); } +#endif +#ifndef TXM_TRACE_USER_EVENT_INSERT_CALL_NOT_USED /* UINT _tx_trace_user_event_insert( ULONG event_id, -> param_0 ULONG info_field_1, -> param_1 @@ -2994,7 +3177,7 @@ ALIGN_TYPE return_value; if (!TXM_MODULE_MANAGER_ENSURE_INSIDE_MODULE_DATA(module_instance, (ALIGN_TYPE)extra_parameters, sizeof(ALIGN_TYPE[3]))) return(TXM_MODULE_INVALID_MEMORY); } - + return_value = (ALIGN_TYPE) _tx_trace_user_event_insert( (ULONG) param_0, (ULONG) param_1, @@ -3004,7 +3187,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MODULE_OBJECT_ALLOCATE_CALL_NOT_USED /* UINT _txm_module_object_allocate( VOID **object_ptr, -> param_0 ULONG object_size -> param_1 @@ -3027,7 +3212,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MODULE_OBJECT_DEALLOCATE_CALL_NOT_USED /* UINT _txm_module_object_deallocate( VOID *object_ptr -> param_0 ); */ @@ -3076,7 +3263,9 @@ ALIGN_TYPE object_pool_end; ); return(return_value); } +#endif +#ifndef TXM_MODULE_OBJECT_POINTER_GET_CALL_NOT_USED /* UINT _txm_module_object_pointer_get( UINT object_type, -> param_0 CHAR *name, -> param_1 @@ -3103,7 +3292,9 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif +#ifndef TXM_MODULE_OBJECT_POINTER_GET_EXTENDED_CALL_NOT_USED /* UINT _txm_module_object_pointer_get_extended( UINT object_type, -> param_0 CHAR *name, -> param_1 @@ -3135,3 +3326,4 @@ ALIGN_TYPE return_value; ); return(return_value); } +#endif diff --git a/common_modules/module_manager/src/txm_module_manager_application_request.c b/common_modules/module_manager/src/txm_module_manager_application_request.c index 7e3491dd..53926e7a 100644 --- a/common_modules/module_manager/src/txm_module_manager_application_request.c +++ b/common_modules/module_manager/src/txm_module_manager_application_request.c @@ -10,70 +10,71 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ /* Include necessary system files. */ #include "tx_api.h" #include "txm_module.h" - - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_application_request PORTABLE C */ -/* 6.1 */ +#ifndef TXM_MODULE_APPLICATION_REQUEST_CALL_NOT_USED +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_application_request PORTABLE C */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes the application-specific module request. */ -/* The entire contents of the request structure is application */ -/* specific and thus the processing in this file is left to the */ -/* application to define. */ -/* */ -/* INPUT */ -/* */ -/* request_id Module request ID */ -/* param_1 First parameter */ -/* param_2 Second parameter */ -/* param_3 Third parameter */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the application-specific module request. */ +/* The entire contents of the request structure is application */ +/* specific and thus the processing in this file is left to the */ +/* application to define. */ +/* */ +/* INPUT */ +/* */ +/* request_id Module request ID */ +/* param_1 First parameter */ +/* param_2 Second parameter */ +/* param_3 Third parameter */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ UINT _txm_module_manager_application_request(ULONG request_id, ALIGN_TYPE param_1, ALIGN_TYPE param_2, ALIGN_TYPE param_3) { - /* By default, simply return the status of not available. */ - return(TX_NOT_AVAILABLE); + return(TX_NOT_AVAILABLE); } - +#endif diff --git a/common_modules/module_manager/src/txm_module_manager_callback_request.c b/common_modules/module_manager/src/txm_module_manager_callback_request.c index efb6f8b5..622f83a4 100644 --- a/common_modules/module_manager/src/txm_module_manager_callback_request.c +++ b/common_modules/module_manager/src/txm_module_manager_callback_request.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -26,45 +26,45 @@ #include "tx_queue.h" #include "tx_thread.h" #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_callback_request PORTABLE C */ + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_callback_request PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function sends a notification callback function request to */ -/* the associated module. */ -/* */ -/* INPUT */ -/* */ -/* module_callback_queue Module callback request queue */ -/* callback_request Callback request */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* tx_queue_send Send module callback request */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function sends a notification callback function request to */ +/* the associated module. */ +/* */ +/* INPUT */ +/* */ +/* module_callback_queue Module callback request queue */ +/* callback_request Callback request */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* tx_queue_send Send module callback request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _txm_module_manager_callback_request(TX_QUEUE *module_callback_queue, TXM_MODULE_CALLBACK_MESSAGE *callback_message) @@ -80,26 +80,26 @@ UINT status; /* Lockout interrupts. */ TX_DISABLE - + /* Determine if the queue is valid. */ if ((module_callback_queue) && (module_callback_queue -> tx_queue_id == TX_QUEUE_ID)) { /* Yes, the queue is valid. */ - + /* Pickup the current callback request in the queue. */ queued_message = (TXM_MODULE_CALLBACK_MESSAGE *) module_callback_queue -> tx_queue_read; - + /* Pickup the number of items enqueued. */ enqueued = module_callback_queue -> tx_queue_enqueued; - + /* Set the found flag to false. */ found = TX_FALSE; /* Loop to look for duplicates in the queue. */ while (enqueued != 0) { - + /* Does this entry match the new callback message? */ if ((queued_message -> txm_module_callback_message_application_function == callback_message -> txm_module_callback_message_application_function) && (queued_message -> txm_module_callback_message_param_1 == callback_message -> txm_module_callback_message_param_1) && @@ -113,62 +113,62 @@ UINT status; (queued_message -> txm_module_callback_message_reserved1 == callback_message -> txm_module_callback_message_reserved1) && (queued_message -> txm_module_callback_message_reserved2 == callback_message -> txm_module_callback_message_reserved2)) { - + /* Update the activation count in the queued request. */ queued_message -> txm_module_callback_message_activation_count++; - + /* Set the found flag to true. */ found = TX_TRUE; - + /* Get out of the loop. */ break; } - + /* Decrease the number of messages to examine. */ enqueued--; - + /* Move the callback message to the next message. */ queued_message++; - + /* Check for wrap? */ if (((ULONG *) queued_message) >= module_callback_queue -> tx_queue_end) { - + /* Yes, set the queued message to the beginning of the queue. */ queued_message = (TXM_MODULE_CALLBACK_MESSAGE *) module_callback_queue -> tx_queue_start; - } + } } /* Restore interrupts. */ TX_RESTORE - + /* Determine if we need to send the new callback request. */ if (found == TX_FALSE) { - + /* Yes, send the message. */ status = _tx_queue_send(module_callback_queue, (VOID *) callback_message, TX_NO_WAIT); - + /* Determine if an error was detected. */ if (status != TX_SUCCESS) { - + /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; } } - + /* Increment the total number of callbacks. */ _txm_module_manager_callback_total_count++; } else { - + /* Module instance is not valid. */ /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; - + /* Restore interrupts. */ TX_RESTORE } diff --git a/common_modules/module_manager/src/txm_module_manager_event_flags_notify_trampoline.c b/common_modules/module_manager/src/txm_module_manager_event_flags_notify_trampoline.c index b86c9e32..7e86e7f7 100644 --- a/common_modules/module_manager/src/txm_module_manager_event_flags_notify_trampoline.c +++ b/common_modules/module_manager/src/txm_module_manager_event_flags_notify_trampoline.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -27,45 +27,45 @@ #include "tx_event_flags.h" #include "tx_thread.h" #include "txm_module.h" - + #ifndef TX_DISABLE_NOTIFY_CALLBACKS -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_event_flags_notify_trampoline PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_event_flags_notify_trampoline PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes the event flags set notification call from */ -/* ThreadX. */ -/* */ -/* INPUT */ -/* */ -/* group_ptr Event flags group pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_manager_callback_request Send module callback request */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the event flags set notification call from */ +/* ThreadX. */ +/* */ +/* INPUT */ +/* */ +/* group_ptr Event flags group pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_callback_request Send module callback request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _txm_module_manager_event_flags_notify_trampoline(TX_EVENT_FLAGS_GROUP *group_ptr) @@ -77,9 +77,9 @@ TXM_MODULE_INSTANCE *module_instance; TXM_MODULE_CALLBACK_MESSAGE callback_message; TX_QUEUE *module_callback_queue; - + /* We now know the callback is for a module. */ - + /* Disable interrupts. */ TX_DISABLE @@ -90,9 +90,9 @@ TX_QUEUE *module_callback_queue; if ((module_instance) && (module_instance -> txm_module_instance_id == TXM_MODULE_ID) && (module_instance -> txm_module_instance_state == TXM_MODULE_STARTED)) { - - /* Yes, the module is still valid. */ - + + /* Yes, the module is still valid. */ + /* Pickup the module's callback message queue. */ module_callback_queue = &(module_instance -> txm_module_instance_callback_request_queue); @@ -113,21 +113,21 @@ TX_QUEUE *module_callback_queue; /* Restore interrupts. */ TX_RESTORE - - /* Call the general processing that will place the callback on the + + /* Call the general processing that will place the callback on the module's callback request queue. */ _txm_module_manager_callback_request(module_callback_queue, &callback_message); } else { - + /* Module no longer valid. */ - + /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; /* Restore interrupts. */ TX_RESTORE } -} +} #endif diff --git a/common_modules/module_manager/src/txm_module_manager_file_load.c b/common_modules/module_manager/src/txm_module_manager_file_load.c index 34053393..b44a533b 100644 --- a/common_modules/module_manager/src/txm_module_manager_file_load.c +++ b/common_modules/module_manager/src/txm_module_manager_file_load.c @@ -80,7 +80,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_file_load(TXM_MODULE_INSTANCE *module_instance, CHAR *module_name, FX_MEDIA *media_ptr, CHAR *file_name) diff --git a/common_modules/module_manager/src/txm_module_manager_in_place_load.c b/common_modules/module_manager/src/txm_module_manager_in_place_load.c index ae482ec1..5826536f 100644 --- a/common_modules/module_manager/src/txm_module_manager_in_place_load.c +++ b/common_modules/module_manager/src/txm_module_manager_in_place_load.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -31,46 +31,46 @@ #include "txm_module_manager_util.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_in_place_load PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_in_place_load PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ +/* DESCRIPTION */ +/* */ /* This function ensures the code-related parts of the module preamble */ /* are valid and calls _txm_module_manager_internal_load to load the */ /* data and prepare the module for execution. */ -/* */ -/* INPUT */ -/* */ -/* module_instance Module instance pointer */ -/* module_name Module name pointer */ -/* module_location Module code location */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* module_name Module name pointer */ +/* module_location Module code location */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ /* _txm_module_manager_internal_load Load data and prepare module for */ /* execution */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_in_place_load(TXM_MODULE_INSTANCE *module_instance, CHAR *module_name, VOID *module_location) diff --git a/common_modules/module_manager/src/txm_module_manager_initialize.c b/common_modules/module_manager/src/txm_module_manager_initialize.c index a9190454..8ea3e693 100644 --- a/common_modules/module_manager/src/txm_module_manager_initialize.c +++ b/common_modules/module_manager/src/txm_module_manager_initialize.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE #define TX_MODULE_MANAGER_INIT @@ -80,7 +80,7 @@ ULONG _txm_module_manger_loaded_count; /* Define the ready flag, which is checked by other module manager APIs to make sure the manager has been initialized. */ - + UINT _txm_module_manager_ready; @@ -97,44 +97,44 @@ ULONG _txm_module_manager_callback_error_count; -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_initialize PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_initialize PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function initializes the module manager. */ -/* */ -/* INPUT */ -/* */ -/* module_memory_start Start of module area */ -/* module_memory_size Size in bytes of module area */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* _tx_byte_pool_create Create module memory byte pool */ -/* _tx_mutex_create Create module manager */ -/* protection mutex */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the module manager. */ +/* */ +/* INPUT */ +/* */ +/* module_memory_start Start of module area */ +/* module_memory_size Size in bytes of module area */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_byte_pool_create Create module memory byte pool */ +/* _tx_mutex_create Create module manager */ +/* protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_initialize(VOID *module_memory_start, ULONG module_memory_size) @@ -143,11 +143,11 @@ UINT _txm_module_manager_initialize(VOID *module_memory_start, ULONG module_mem /* Check for interrupt call. */ if (TX_THREAD_GET_SYSTEM_STATE() != 0) { - + /* Now, make sure the call is from an interrupt and not initialization. */ if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS) { - + /* Invalid caller of this function, return appropriate error code. */ return(TX_CALLER_ERROR); } @@ -171,7 +171,7 @@ UINT _txm_module_manager_initialize(VOID *module_memory_start, ULONG module_mem /* Create the module manager protection mutex. */ _tx_mutex_create(&_txm_module_manager_mutex, "Module Manager Protection Mutex", TX_NO_INHERIT); - /* Create a byte pool for allocating RAM areas for modules. */ + /* Create a byte pool for allocating RAM areas for modules. */ _tx_byte_pool_create(&_txm_module_manager_byte_pool, "Module Manager Byte Pool", module_memory_start, module_memory_size); /* Indicate the module manager object pool has not been created. */ @@ -179,7 +179,7 @@ UINT _txm_module_manager_initialize(VOID *module_memory_start, ULONG module_mem /* Mark the module manager as ready! */ _txm_module_manager_ready = TX_TRUE; - + /* Return success. */ return(TX_SUCCESS); } diff --git a/common_modules/module_manager/src/txm_module_manager_internal_load.c b/common_modules/module_manager/src/txm_module_manager_internal_load.c index 6b5ae54f..367b557e 100644 --- a/common_modules/module_manager/src/txm_module_manager_internal_load.c +++ b/common_modules/module_manager/src/txm_module_manager_internal_load.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -31,49 +31,49 @@ #include "txm_module_manager_util.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_internal_load PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_internal_load PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function allocates data memory for module and prepares the */ -/* module for execution from the supplied code location. */ -/* */ -/* INPUT */ -/* */ -/* module_instance Module instance pointer */ -/* module_name Module name pointer */ -/* module_location Module code location */ -/* code_size Module code size */ -/* code_allocation_ptr Allocated code location */ -/* code_allocation_size Allocated code size */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* _tx_byte_allocate Allocate data area */ -/* _tx_mutex_get Get protection mutex */ -/* _tx_mutex_put Release protection mutex */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates data memory for module and prepares the */ +/* module for execution from the supplied code location. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* module_name Module name pointer */ +/* module_location Module code location */ +/* code_size Module code size */ +/* code_allocation_ptr Allocated code location */ +/* code_allocation_size Allocated code size */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_byte_allocate Allocate data area */ +/* _tx_mutex_get Get protection mutex */ +/* _tx_mutex_put Release protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_internal_load(TXM_MODULE_INSTANCE *module_instance, CHAR *module_name, VOID *module_location, @@ -81,7 +81,7 @@ UINT _txm_module_manager_internal_load(TXM_MODULE_INSTANCE *module_instance, CH { TX_INTERRUPT_SAVE_AREA - + TXM_MODULE_PREAMBLE *module_preamble; TXM_MODULE_INSTANCE *next_module, *previous_module; ULONG shell_function_adjust; @@ -104,11 +104,11 @@ UINT status; /* Check for interrupt call. */ if (TX_THREAD_GET_SYSTEM_STATE() != 0) { - + /* Now, make sure the call is from an interrupt and not initialization. */ if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS) { - + /* Invalid caller of this function, return appropriate error code. */ return(TX_CALLER_ERROR); } @@ -117,15 +117,15 @@ UINT status; /* Determine if the module manager has not been initialized yet. */ if (_txm_module_manager_ready != TX_TRUE) { - + /* Module manager has not been initialized. */ - return(TX_NOT_AVAILABLE); + return(TX_NOT_AVAILABLE); } /* Determine if the module is valid. */ if (module_instance == TX_NULL) { - + /* Invalid module pointer. */ return(TX_PTR_ERROR); } @@ -150,7 +150,7 @@ UINT status; /* Check to make sure there is a valid module to load. */ if (module_preamble -> txm_module_preamble_id != TXM_MODULE_ID) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -175,12 +175,12 @@ UINT status; /* Invalid properties. Return error. */ return(TXM_MODULE_INVALID_PROPERTIES); } - + /* Check for valid module entry offsets. */ if ((module_preamble -> txm_module_preamble_shell_entry_function == 0) || (module_preamble -> txm_module_preamble_start_function == 0)) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -194,7 +194,7 @@ UINT status; (module_preamble -> txm_module_preamble_start_stop_stack_size == 0) || (module_preamble -> txm_module_preamble_callback_stack_size == 0)) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -227,11 +227,11 @@ UINT status; /* Update the data size to account for the default thread stacks. */ TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(data_size, start_stop_stack_size, data_size); TXM_MODULE_MANAGER_UTIL_MATH_ADD_ULONG(data_size, callback_stack_size, data_size); - + /* Setup the default code and data alignments. */ data_alignment = (ULONG) TXM_MODULE_DATA_ALIGNMENT; - /* Get the port-specific alignment for the data size. Note we only want data + /* Get the port-specific alignment for the data size. Note we only want data so we pass values of 1 for code (to avoid any possible div by 0 errors). */ code_size_ignored = 1; code_alignment_ignored = 1; @@ -239,16 +239,16 @@ UINT status; /* Calculate the module's total RAM memory requirement. This entire area is allocated from the module manager's byte pool. The general layout is defined as follows: - + Lowest Address: Start of start/stop thread stack ... [note: thread entry info is embedded near end of stack areas] - End of start/stop thread stack - + End of start/stop thread stack + Start of callback thread stack ... [note: thread entry info is embedded near end of stack areas] - End of callback thread stack - - Module's Data Area + End of callback thread stack + + Module's Data Area ... End of Module's Data Area Highest Address: */ @@ -258,11 +258,11 @@ UINT status; /* Allocate memory for the module. */ status = _tx_byte_allocate(&_txm_module_manager_byte_pool, (VOID **) &memory_ptr, data_allocation_size, TX_NO_WAIT); - + /* Determine if the module memory allocation was successful. */ if (status) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -272,10 +272,10 @@ UINT status; /* Clear the allocated memory. */ TX_MEMSET(memory_ptr, ((UCHAR) 0), data_allocation_size); - + /* Disable interrupts. */ TX_DISABLE - + /* Setup the module instance structure. */ module_instance -> txm_module_instance_id = TXM_MODULE_ID; @@ -285,7 +285,7 @@ UINT status; /* Save the module properties. */ module_instance -> txm_module_instance_property_flags = module_preamble -> txm_module_preamble_property_flags; - /* Set the module data memory allocation. This is the address released + /* Set the module data memory allocation. This is the address released when the module is unloaded. */ module_instance -> txm_module_instance_data_allocation_ptr = (VOID *) memory_ptr; @@ -297,14 +297,14 @@ UINT status; data_start = (data_start + (((ALIGN_TYPE)data_alignment) - 1)) & ~(((ALIGN_TYPE)data_alignment) - 1); memory_ptr = (CHAR *) data_start; module_instance -> txm_module_instance_data_start = (VOID *) memory_ptr; - + /* Compute the end of the data memory allocation. */ module_instance -> txm_module_instance_data_end = (VOID *) (memory_ptr + (data_size - 1)); /* Save the size of the data area. */ module_instance -> txm_module_instance_data_size = data_size; - /* Set the module code memory allocation. This is the address released + /* Set the module code memory allocation. This is the address released when the module is unloaded. */ module_instance -> txm_module_instance_code_allocation_ptr = (VOID *) code_allocation_ptr; @@ -329,7 +329,7 @@ UINT status; /* Save the module application ID in the module instance. */ module_instance -> txm_module_instance_application_module_id = module_preamble -> txm_module_preamble_application_module_id; - + /* Setup the module's start/stop thread stack area. */ module_instance -> txm_module_instance_start_stop_stack_start_address = (VOID *) (memory_ptr); module_instance -> txm_module_instance_start_stop_stack_size = start_stop_stack_size; @@ -358,36 +358,36 @@ UINT status; /* Calculate the function adjustments based on the specific implementation of the module manager/module. */ TXM_MODULE_MANAGER_CALCULATE_ADJUSTMENTS(module_preamble -> txm_module_preamble_property_flags, shell_function_adjust, start_function_adjust, stop_function_adjust, callback_function_adjust) - /* Build actual addresses based on load... Setup all the function pointers. Any adjustments needed to shell entry, start function, and callback function are defined in the + /* Build actual addresses based on load... Setup all the function pointers. Any adjustments needed to shell entry, start function, and callback function are defined in the module preamble. */ - module_instance -> txm_module_instance_shell_entry_function = (VOID (*)(TX_THREAD *, TXM_MODULE_INSTANCE *)) (((CHAR *) module_instance -> txm_module_instance_code_start) + - (module_preamble -> txm_module_preamble_shell_entry_function) + + module_instance -> txm_module_instance_shell_entry_function = (VOID (*)(TX_THREAD *, TXM_MODULE_INSTANCE *)) (((CHAR *) module_instance -> txm_module_instance_code_start) + + (module_preamble -> txm_module_preamble_shell_entry_function) + (shell_function_adjust)); - module_instance -> txm_module_instance_start_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) + - (module_preamble -> txm_module_preamble_start_function) + + module_instance -> txm_module_instance_start_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) + + (module_preamble -> txm_module_preamble_start_function) + (start_function_adjust)); - module_instance -> txm_module_instance_callback_request_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) + + module_instance -> txm_module_instance_callback_request_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) + (module_preamble -> txm_module_preamble_callback_function) + - (callback_function_adjust)); + (callback_function_adjust)); /* Determine if there is a stop function for this module. */ if (module_preamble -> txm_module_preamble_stop_function) { - + /* Yes, there is a stop function, build the address. */ - module_instance -> txm_module_instance_stop_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) + - (module_preamble -> txm_module_preamble_stop_function) + + module_instance -> txm_module_instance_stop_thread_entry = (VOID (*)(ULONG)) (((CHAR *) module_instance -> txm_module_instance_code_start) + + (module_preamble -> txm_module_preamble_stop_function) + (stop_function_adjust)); } else { - + /* No, there is no stop function. Just set the pointer to NULL. */ module_instance -> txm_module_instance_stop_thread_entry = TX_NULL; } - + /* Load the module control block with port-specific information. */ TXM_MODULE_MANAGER_MODULE_SETUP(module_instance); - + /* Now add the module to the linked list of created modules. */ if (_txm_module_manger_loaded_count++ == 0) { @@ -410,7 +410,7 @@ UINT status; /* Setup this module's created links. */ module_instance -> txm_module_instance_loaded_previous = previous_module; - module_instance -> txm_module_instance_loaded_next = next_module; + module_instance -> txm_module_instance_loaded_next = next_module; } /* Restore interrupts. */ diff --git a/common_modules/module_manager/src/txm_module_manager_kernel_dispatch.c b/common_modules/module_manager/src/txm_module_manager_kernel_dispatch.c index a0929621..6839194a 100644 --- a/common_modules/module_manager/src/txm_module_manager_kernel_dispatch.c +++ b/common_modules/module_manager/src/txm_module_manager_kernel_dispatch.c @@ -29,7 +29,7 @@ #include "tx_queue.h" #include "tx_mutex.h" #include "tx_semaphore.h" -#include "tx_thread.h" +#include "tx_thread.h" #include "tx_timer.h" #include "tx_trace.h" #include "txm_module.h" @@ -41,7 +41,7 @@ /* FUNCTION RELEASE */ /* */ /* _txm_module_manager_kernel_dispatch PORTABLE C */ -/* 6.1.6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -89,6 +89,9 @@ /* added optional defines to */ /* remove unneeded functions, */ /* resulting in version 6.1.6 */ +/* 01-31-2022 Scott Larson Modified comments and added */ +/* CALL_NOT_USED option, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ ALIGN_TYPE _txm_module_manager_kernel_dispatch(ULONG kernel_request, ALIGN_TYPE param_0, ALIGN_TYPE param_1, ALIGN_TYPE param_2) @@ -437,7 +440,7 @@ TXM_MODULE_INSTANCE *module_instance; break; } #endif - + #ifndef TXM_QUEUE_SEND_CALL_NOT_USED case TXM_QUEUE_SEND_CALL: { @@ -888,14 +891,16 @@ TXM_MODULE_INSTANCE *module_instance; return_value = (ALIGN_TYPE) _txm_module_manager_port_dispatch(module_instance, kernel_request, param_0, param_1, param_2); } #endif - - /* Determine if an application request is present. */ + + #ifndef TXM_MODULE_APPLICATION_REQUEST_CALL_NOT_USED + /* Determine if an application request is present. */ if (kernel_request >= TXM_APPLICATION_REQUEST_ID_BASE) { /* Yes, call the module manager function that the application defines in order to support application-specific requests. */ return_value = (ALIGN_TYPE) _txm_module_manager_application_request(kernel_request-TXM_APPLICATION_REQUEST_ID_BASE, param_0, param_1, param_2); } + #endif #ifdef TXM_MODULE_ENABLE_NETX /* Determine if there is a NetX request. */ diff --git a/common_modules/module_manager/src/txm_module_manager_maximum_module_priority_set.c b/common_modules/module_manager/src/txm_module_manager_maximum_module_priority_set.c index d725faf2..94367983 100644 --- a/common_modules/module_manager/src/txm_module_manager_maximum_module_priority_set.c +++ b/common_modules/module_manager/src/txm_module_manager_maximum_module_priority_set.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -27,43 +27,43 @@ #include "txm_module.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_maximum_module_priority_set PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_maximum_module_priority_set PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function sets the maximum thread priority allowed in a module. */ -/* */ -/* INPUT */ -/* */ -/* module_instance Module instance pointer */ -/* priority Maximum thread priority */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* _tx_mutex_get Get protection mutex */ -/* _tx_mutex_put Release protection mutex */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the maximum thread priority allowed in a module. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* priority Maximum thread priority */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_mutex_get Get protection mutex */ +/* _tx_mutex_put Release protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_maximum_module_priority_set(TXM_MODULE_INSTANCE *module_instance, UINT priority) @@ -72,45 +72,45 @@ UINT _txm_module_manager_maximum_module_priority_set(TXM_MODULE_INSTANCE *module if (_txm_module_manager_ready != TX_TRUE) { /* Module manager has not been initialized. */ - return(TX_NOT_AVAILABLE); + return(TX_NOT_AVAILABLE); } - + /* Determine if the module is valid. */ if (module_instance == TX_NULL) { /* Invalid module pointer. */ return(TX_PTR_ERROR); } - + /* Get module manager protection mutex. */ _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); - + /* Determine if the module instance is valid. */ if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) { /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); - + /* Invalid module pointer. */ return(TX_PTR_ERROR); } - + /* Determine if the module instance is in the loaded state. */ if ((module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) && (module_instance -> txm_module_instance_state != TXM_MODULE_STOPPED)) { /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); - + /* Return error if the module is not ready. */ return(TX_START_ERROR); } - - + + /* Set module's maximum priority. */ module_instance->txm_module_instance_maximum_priority = priority; - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); - + return(TX_SUCCESS); } diff --git a/common_modules/module_manager/src/txm_module_manager_memory_load.c b/common_modules/module_manager/src/txm_module_manager_memory_load.c index e35f01b9..8d35c554 100644 --- a/common_modules/module_manager/src/txm_module_manager_memory_load.c +++ b/common_modules/module_manager/src/txm_module_manager_memory_load.c @@ -71,7 +71,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_memory_load(TXM_MODULE_INSTANCE *module_instance, CHAR *module_name, VOID *module_location) diff --git a/common_modules/module_manager/src/txm_module_manager_object_allocate.c b/common_modules/module_manager/src/txm_module_manager_object_allocate.c index 829dbb1f..0482fbcf 100644 --- a/common_modules/module_manager/src/txm_module_manager_object_allocate.c +++ b/common_modules/module_manager/src/txm_module_manager_object_allocate.c @@ -28,7 +28,7 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _txm_module_manager_object_allocate PORTABLE C */ +/* _txm_module_manager_object_allocate PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ @@ -36,16 +36,16 @@ /* */ /* DESCRIPTION */ /* */ -/* This function allocates memory for an object from the memory pool */ -/* supplied to txm_module_manager_initialize. */ +/* This function allocates memory for an object from the memory pool */ +/* supplied to txm_module_manager_initialize. */ /* */ /* INPUT */ /* */ -/* object_ptr Destination of object pointer on */ -/* successful allocation */ -/* object_size Size in bytes of the object to be */ -/* allocated */ -/* module_instance The module instance that the */ +/* object_ptr Destination of object pointer on */ +/* successful allocation */ +/* object_size Size in bytes of the object to be */ +/* allocated */ +/* module_instance The module instance that the */ /* object belongs to */ /* */ /* OUTPUT */ @@ -66,7 +66,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_allocate(VOID **object_ptr_ptr, ULONG object_size, TXM_MODULE_INSTANCE *module_instance) @@ -98,7 +98,7 @@ UINT return_value; /* Allocate the object requested by the module - adding an extra ULONG in order to store the module instance pointer. */ - return_value = (ULONG) _txe_byte_allocate(&_txm_module_manager_object_pool, (VOID **) &object_ptr, + return_value = (ULONG) _txe_byte_allocate(&_txm_module_manager_object_pool, (VOID **) &object_ptr, (ULONG) (object_size + sizeof(TXM_MODULE_ALLOCATED_OBJECT)), TX_NO_WAIT); /* Determine if the request was successful. */ diff --git a/common_modules/module_manager/src/txm_module_manager_object_deallocate.c b/common_modules/module_manager/src/txm_module_manager_object_deallocate.c index 5b90b995..fd9fefdb 100644 --- a/common_modules/module_manager/src/txm_module_manager_object_deallocate.c +++ b/common_modules/module_manager/src/txm_module_manager_object_deallocate.c @@ -23,7 +23,7 @@ #define TX_SOURCE_CODE #include "tx_api.h" -#include "tx_thread.h" +#include "tx_thread.h" #include "txm_module.h" /**************************************************************************/ @@ -38,11 +38,11 @@ /* */ /* DESCRIPTION */ /* */ -/* This function deallocates a previously allocated object. */ +/* This function deallocates a previously allocated object. */ /* */ /* INPUT */ /* */ -/* object_ptr Object pointer to deallocate */ +/* object_ptr Object pointer to deallocate */ /* */ /* OUTPUT */ /* */ @@ -63,7 +63,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_deallocate(VOID *object_ptr) @@ -78,18 +78,18 @@ UINT return_value; /* Determine if an object pool was created. */ if (_txm_module_manager_object_pool_created) { - + TXM_MODULE_ALLOCATED_OBJECT *next_object, *previous_object; - + /* Pickup module instance pointer. */ module_instance = _tx_thread_current_ptr -> tx_thread_module_instance_ptr; - + /* Setup the memory pointer. */ module_allocated_object_ptr = (TXM_MODULE_ALLOCATED_OBJECT *) object_ptr; - + /* Position the object pointer backwards to position back to the module manager information. */ previous_object = module_allocated_object_ptr--; - + /* Make sure the object is valid. */ if ((module_allocated_object_ptr == TX_NULL) || (module_allocated_object_ptr -> txm_module_allocated_object_module_instance != module_instance) || (module_instance -> txm_module_instance_object_list_count == 0)) { @@ -98,8 +98,8 @@ UINT return_value; } else { - - /* Unlink the node. */ + + /* Unlink the node. */ if ((--module_instance -> txm_module_instance_object_list_count) == 0) { /* Only allocated object, just set the allocated list to NULL. */ @@ -112,16 +112,16 @@ UINT return_value; previous_object = module_allocated_object_ptr -> txm_module_allocated_object_previous; next_object -> txm_module_allocated_object_previous = previous_object; previous_object -> txm_module_allocated_object_next = next_object; - + /* See if we have to update the allocated object list head pointer. */ if (module_instance -> txm_module_instance_object_list_head == module_allocated_object_ptr) { /* Yes, move the head pointer to the next link. */ - module_instance -> txm_module_instance_object_list_head = next_object; + module_instance -> txm_module_instance_object_list_head = next_object; } } - - /* Release the object memory. */ + + /* Release the object memory. */ return_value = (ULONG) _txe_byte_release((VOID *) module_allocated_object_ptr); } } @@ -130,9 +130,9 @@ UINT return_value; /* Set return value to not enabled. */ return_value = TX_NOT_AVAILABLE; } - + /* Release the protection mutex. */ _txe_mutex_put(&_txm_module_manager_mutex); - + return(return_value); } diff --git a/common_modules/module_manager/src/txm_module_manager_object_pointer_get.c b/common_modules/module_manager/src/txm_module_manager_object_pointer_get.c index 6a7927ea..dd58ff82 100644 --- a/common_modules/module_manager/src/txm_module_manager_object_pointer_get.c +++ b/common_modules/module_manager/src/txm_module_manager_object_pointer_get.c @@ -76,7 +76,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_pointer_get(UINT object_type, CHAR *name, VOID **object_ptr) @@ -84,7 +84,7 @@ UINT _txm_module_manager_object_pointer_get(UINT object_type, CHAR *name, VOID UINT status; - /* Call the secure version of this function with the maximum length + /* Call the secure version of this function with the maximum length possible since none was passed. */ status = _txm_module_manager_object_pointer_get_extended(object_type, name, TXM_MODULE_MANAGER_UTIL_MAX_VALUE_OF_TYPE_UNSIGNED(UINT), object_ptr); return(status); diff --git a/common_modules/module_manager/src/txm_module_manager_object_pointer_get_extended.c b/common_modules/module_manager/src/txm_module_manager_object_pointer_get_extended.c index b62081ac..5ff38ccf 100644 --- a/common_modules/module_manager/src/txm_module_manager_object_pointer_get_extended.c +++ b/common_modules/module_manager/src/txm_module_manager_object_pointer_get_extended.c @@ -93,14 +93,14 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_pointer_get_extended(UINT object_type, CHAR *search_name, UINT search_name_length, VOID **object_ptr) { - + TX_INTERRUPT_SAVE_AREA - + TX_THREAD *thread_ptr; TX_TIMER *timer_ptr; TX_QUEUE *queue_ptr; @@ -117,7 +117,7 @@ TXM_MODULE_INSTANCE *module_instance; /* Determine if the name or object pointer are NULL. */ if ((search_name == TX_NULL) || (object_ptr == TX_NULL)) { - + /* Return error! */ return(TX_PTR_ERROR); } @@ -133,36 +133,36 @@ TXM_MODULE_INSTANCE *module_instance; /* Temporarily disable preemption. This will keep other threads from creating and deleting threads. */ _tx_thread_preempt_disable++; - + /* Restore interrupts. */ TX_RESTORE - + /* Process relative to the object type. */ switch(object_type) { - + /* Determine if a thread object is requested. */ case TXM_THREAD_OBJECT: - { - + { + /* Loop to find the first matching thread. */ i = 0; thread_ptr = _tx_thread_created_ptr; while (i < _tx_thread_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, thread_ptr -> tx_thread_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) thread_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; @@ -171,32 +171,32 @@ TXM_MODULE_INSTANCE *module_instance; } break; } - + /* Determine if a timer object is requested. */ case TXM_TIMER_OBJECT: { - + /* Loop to find the first matching timer. */ i = 0; timer_ptr = _tx_timer_created_ptr; while (i < _tx_timer_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, timer_ptr -> tx_timer_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) timer_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; - + /* Move to next timer. */ timer_ptr = timer_ptr -> tx_timer_created_next; } @@ -212,22 +212,22 @@ TXM_MODULE_INSTANCE *module_instance; queue_ptr = _tx_queue_created_ptr; while (i < _tx_queue_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, queue_ptr -> tx_queue_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) queue_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } /* Increment the counter. */ i++; - + /* Move to next queue. */ queue_ptr = queue_ptr -> tx_queue_created_next; } @@ -243,22 +243,22 @@ TXM_MODULE_INSTANCE *module_instance; events_ptr = _tx_event_flags_created_ptr; while (i < _tx_event_flags_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, events_ptr -> tx_event_flags_group_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) events_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; - + /* Move to next event flags group. */ events_ptr = events_ptr -> tx_event_flags_group_created_next; } @@ -274,22 +274,22 @@ TXM_MODULE_INSTANCE *module_instance; semaphore_ptr = _tx_semaphore_created_ptr; while (i < _tx_semaphore_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, semaphore_ptr -> tx_semaphore_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) semaphore_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; - + /* Move to next semaphore. */ semaphore_ptr = semaphore_ptr -> tx_semaphore_created_next; } @@ -305,22 +305,22 @@ TXM_MODULE_INSTANCE *module_instance; mutex_ptr = _tx_mutex_created_ptr; while (i < _tx_mutex_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, mutex_ptr -> tx_mutex_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) mutex_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; - + /* Move to next mutex. */ mutex_ptr = mutex_ptr -> tx_mutex_created_next; } @@ -353,22 +353,22 @@ TXM_MODULE_INSTANCE *module_instance; block_pool_ptr = _tx_block_pool_created_ptr; while (i < _tx_block_pool_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, block_pool_ptr -> tx_block_pool_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) block_pool_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; - + /* Move to next block pool. */ block_pool_ptr = block_pool_ptr -> tx_block_pool_created_next; } @@ -401,22 +401,22 @@ TXM_MODULE_INSTANCE *module_instance; byte_pool_ptr = _tx_byte_pool_created_ptr; while (i < _tx_byte_pool_created_count) { - + /* Do we have a match? */ if (_txm_module_manager_object_name_compare(search_name, search_name_length, byte_pool_ptr -> tx_byte_pool_name)) { - + /* Yes, we found it - return the necessary info! */ *object_ptr = (VOID *) byte_pool_ptr; - + /* Set the the status to success! */ - status = TX_SUCCESS; + status = TX_SUCCESS; break; } - + /* Increment the counter. */ i++; - + /* Move to next byte pool. */ byte_pool_ptr = byte_pool_ptr -> tx_byte_pool_created_next; } @@ -435,7 +435,7 @@ TXM_MODULE_INSTANCE *module_instance; /* Determine if there is a NetX object get request. */ if ((object_type >= TXM_NETX_OBJECTS_START) && (object_type < TXM_NETX_OBJECTS_END)) { - + /* Call the NetX module object get function. */ status = _txm_module_manager_netx_object_pointer_get(object_type, search_name, search_name_length, object_ptr); } @@ -446,7 +446,7 @@ TXM_MODULE_INSTANCE *module_instance; /* Determine if there is a NetX Duo object get request. */ if ((object_type >= TXM_NETXDUO_OBJECTS_START) && (object_type < TXM_NETXDUO_OBJECTS_END)) { - + /* Call the NetX Duo module object get function. */ status = _txm_module_manager_netxduo_object_pointer_get(object_type, search_name, search_name_length, object_ptr); } @@ -457,7 +457,7 @@ TXM_MODULE_INSTANCE *module_instance; /* Determine if there is a FileX object get request. */ if ((object_type >= TXM_FILEX_OBJECTS_START) && (object_type < TXM_FILEX_OBJECTS_END)) { - + /* Call the FileX module object get function. */ status = _txm_module_manager_filex_object_pointer_get(object_type, search_name, search_name_length, object_ptr); } @@ -469,7 +469,7 @@ TXM_MODULE_INSTANCE *module_instance; /* Determine if there is a GUIX object get request. */ if ((object_type >= TXM_GUIX_OBJECTS_START) && (object_type < TXM_GUIX_OBJECTS_END)) { - + /* Call the GUIX module object get function. */ status = _txm_module_manager_guix_object_pointer_get(object_type, search_name, search_name_length, object_ptr); } @@ -480,13 +480,13 @@ TXM_MODULE_INSTANCE *module_instance; /* Determine if there is a USBX object get request. */ if ((object_type >= TXM_USBX_OBJECTS_START) && (object_type < TXM_USBX_OBJECTS_END)) { - + /* Call the USBX object get function. */ status = _txm_module_manager_usbx_object_pointer_get(object_type, search_name, search_name_length, object_ptr); } #endif - break; + break; } /* Disable interrupts. */ diff --git a/common_modules/module_manager/src/txm_module_manager_object_pool_create.c b/common_modules/module_manager/src/txm_module_manager_object_pool_create.c index 25a91340..9090432a 100644 --- a/common_modules/module_manager/src/txm_module_manager_object_pool_create.c +++ b/common_modules/module_manager/src/txm_module_manager_object_pool_create.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -26,56 +26,56 @@ #include "txm_module.h" #include "tx_byte_pool.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* txm_module_manager_object_pool_create PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* txm_module_manager_object_pool_create PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function creates an object pool for the module manager, */ -/* which is used by modules to allocate system resources outside */ -/* the memory area of the module. This is especially useful in */ -/* memory protection. */ -/* */ -/* INPUT */ -/* */ -/* object_memory Object memory address */ -/* object_memory_size Size in bytes of memory area */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* _tx_byte_pool_create Create module memory byte pool */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function creates an object pool for the module manager, */ +/* which is used by modules to allocate system resources outside */ +/* the memory area of the module. This is especially useful in */ +/* memory protection. */ +/* */ +/* INPUT */ +/* */ +/* object_memory Object memory address */ +/* object_memory_size Size in bytes of memory area */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_byte_pool_create Create module memory byte pool */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_pool_create(VOID *object_memory, ULONG object_memory_size) { - /* Create a byte pool for allocating RAM areas for modules. */ + /* Create a byte pool for allocating RAM areas for modules. */ _tx_byte_pool_create(&_txm_module_manager_object_pool, "Module Manager Object Pool", object_memory, object_memory_size); /* Indicate the module manager object pool has been created. */ _txm_module_manager_object_pool_created = TX_TRUE; - + /* Return success. */ return(TX_SUCCESS); } diff --git a/common_modules/module_manager/src/txm_module_manager_properties_get.c b/common_modules/module_manager/src/txm_module_manager_properties_get.c index 3a91812d..d46afa75 100644 --- a/common_modules/module_manager/src/txm_module_manager_properties_get.c +++ b/common_modules/module_manager/src/txm_module_manager_properties_get.c @@ -10,57 +10,57 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE #include "txm_module.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_properties_get PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_properties_get PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function returns the properties of the specified module so they*/ -/* may be checked before executing the module. */ -/* */ -/* INPUT */ -/* */ -/* module_instance Module instance pointer */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the properties of the specified module so they*/ +/* may be checked before executing the module. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_properties_get(TXM_MODULE_INSTANCE *module_instance, ULONG *module_properties_ptr) @@ -69,15 +69,15 @@ UINT _txm_module_manager_properties_get(TXM_MODULE_INSTANCE *module_instance, U /* Determine if the module manager has not been initialized yet. */ if (_txm_module_manager_ready != TX_TRUE) { - + /* Module manager has not been initialized. */ - return(TX_NOT_AVAILABLE); + return(TX_NOT_AVAILABLE); } /* Determine if the module is valid. */ if (module_instance == TX_NULL) { - + /* Invalid module pointer. */ return(TX_PTR_ERROR); } diff --git a/common_modules/module_manager/src/txm_module_manager_queue_notify_trampoline.c b/common_modules/module_manager/src/txm_module_manager_queue_notify_trampoline.c index 436bec4e..453e88f0 100644 --- a/common_modules/module_manager/src/txm_module_manager_queue_notify_trampoline.c +++ b/common_modules/module_manager/src/txm_module_manager_queue_notify_trampoline.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -28,41 +28,41 @@ #include "txm_module.h" #ifndef TX_DISABLE_NOTIFY_CALLBACKS -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_queue_notify_trampoline PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_queue_notify_trampoline PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes the queue notification call from ThreadX. */ -/* */ -/* INPUT */ -/* */ -/* queue_ptr Queue pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_manager_callback_request Send module callback request */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the queue notification call from ThreadX. */ +/* */ +/* INPUT */ +/* */ +/* queue_ptr Queue pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_callback_request Send module callback request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _txm_module_manager_queue_notify_trampoline(TX_QUEUE *queue_ptr) @@ -74,9 +74,9 @@ TXM_MODULE_INSTANCE *module_instance; TXM_MODULE_CALLBACK_MESSAGE callback_message; TX_QUEUE *module_callback_queue; - + /* We now know the callback is for a module. */ - + /* Disable interrupts. */ TX_DISABLE @@ -87,9 +87,9 @@ TX_QUEUE *module_callback_queue; if ((module_instance) && (module_instance -> txm_module_instance_id == TXM_MODULE_ID) && (module_instance -> txm_module_instance_state == TXM_MODULE_STARTED)) { - - /* Yes, the module is still valid. */ - + + /* Yes, the module is still valid. */ + /* Pickup the module's callback message queue. */ module_callback_queue = &(module_instance -> txm_module_instance_callback_request_queue); @@ -107,26 +107,26 @@ TX_QUEUE *module_callback_queue; callback_message.txm_module_callback_message_param_8 = 0; callback_message.txm_module_callback_message_reserved1 = 0; callback_message.txm_module_callback_message_reserved2 = 0; - + /* Restore interrupts. */ TX_RESTORE - - /* Call the general processing that will place the callback on the + + /* Call the general processing that will place the callback on the module's callback request queue. */ _txm_module_manager_callback_request(module_callback_queue, &callback_message); } else { - + /* Module no longer valid. */ - + /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; /* Restore interrupts. */ TX_RESTORE } -} +} #endif - + diff --git a/common_modules/module_manager/src/txm_module_manager_semaphore_notify_trampoline.c b/common_modules/module_manager/src/txm_module_manager_semaphore_notify_trampoline.c index e90fd28c..4992a0b8 100644 --- a/common_modules/module_manager/src/txm_module_manager_semaphore_notify_trampoline.c +++ b/common_modules/module_manager/src/txm_module_manager_semaphore_notify_trampoline.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -28,42 +28,42 @@ #include "txm_module.h" #ifndef TX_DISABLE_NOTIFY_CALLBACKS -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_semaphore_notify_trampoline PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_semaphore_notify_trampoline PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes the semaphore put notification call from */ -/* ThreadX. */ -/* */ -/* INPUT */ -/* */ -/* semaphore_ptr Semaphore pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_manager_callback_request Send module callback request */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the semaphore put notification call from */ +/* ThreadX. */ +/* */ +/* INPUT */ +/* */ +/* semaphore_ptr Semaphore pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_callback_request Send module callback request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _txm_module_manager_semaphore_notify_trampoline(TX_SEMAPHORE *semaphore_ptr) @@ -75,9 +75,9 @@ TXM_MODULE_INSTANCE *module_instance; TXM_MODULE_CALLBACK_MESSAGE callback_message; TX_QUEUE *module_callback_queue; - + /* We now know the callback is for a module. */ - + /* Disable interrupts. */ TX_DISABLE @@ -88,9 +88,9 @@ TX_QUEUE *module_callback_queue; if ((module_instance) && (module_instance -> txm_module_instance_id == TXM_MODULE_ID) && (module_instance -> txm_module_instance_state == TXM_MODULE_STARTED)) { - - /* Yes, the module is still valid. */ - + + /* Yes, the module is still valid. */ + /* Pickup the module's callback message queue. */ module_callback_queue = &(module_instance -> txm_module_instance_callback_request_queue); @@ -108,24 +108,24 @@ TX_QUEUE *module_callback_queue; callback_message.txm_module_callback_message_param_8 = 0; callback_message.txm_module_callback_message_reserved1 = 0; callback_message.txm_module_callback_message_reserved2 = 0; - + /* Restore interrupts. */ TX_RESTORE - - /* Call the general processing that will place the callback on the + + /* Call the general processing that will place the callback on the module's callback request queue. */ _txm_module_manager_callback_request(module_callback_queue, &callback_message); } else { - + /* Module no longer valid. */ /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; - + /* Restore interrupts. */ TX_RESTORE } -} +} #endif diff --git a/common_modules/module_manager/src/txm_module_manager_start.c b/common_modules/module_manager/src/txm_module_manager_start.c index 365803b5..9d839624 100644 --- a/common_modules/module_manager/src/txm_module_manager_start.c +++ b/common_modules/module_manager/src/txm_module_manager_start.c @@ -75,22 +75,22 @@ /**************************************************************************/ UINT _txm_module_manager_start(TXM_MODULE_INSTANCE *module_instance) { - + UINT status; /* Determine if the module manager has not been initialized yet. */ if (_txm_module_manager_ready != TX_TRUE) { - + /* Module manager has not been initialized. */ - return(TX_NOT_AVAILABLE); + return(TX_NOT_AVAILABLE); } /* Determine if the module is valid. */ if (module_instance == TX_NULL) { - + /* Invalid module pointer. */ return(TX_PTR_ERROR); } @@ -108,11 +108,11 @@ UINT status; /* Invalid module pointer. */ return(TX_PTR_ERROR); } - + /* Determine if the module instance is in the loaded state. */ if ((module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) && (module_instance -> txm_module_instance_state != TXM_MODULE_STOPPED)) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -124,7 +124,7 @@ UINT status; if (module_instance -> txm_module_instance_start_stop_priority < module_instance -> txm_module_instance_maximum_priority || module_instance -> txm_module_instance_callback_priority < module_instance -> txm_module_instance_maximum_priority) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -133,13 +133,13 @@ UINT status; } /* Create the module's callback request queue. */ - status = _tx_queue_create(&(module_instance -> txm_module_instance_callback_request_queue), "Module Callback Request Queue", (sizeof(TXM_MODULE_CALLBACK_MESSAGE)/sizeof(ULONG)), + status = _tx_queue_create(&(module_instance -> txm_module_instance_callback_request_queue), "Module Callback Request Queue", (sizeof(TXM_MODULE_CALLBACK_MESSAGE)/sizeof(ULONG)), module_instance -> txm_module_instance_callback_request_queue_area, sizeof(module_instance -> txm_module_instance_callback_request_queue_area)); /* Determine if there was an error. */ if (status) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -147,7 +147,7 @@ UINT status; return(TX_START_ERROR); } - /* Create the module start thread. */ + /* Create the module start thread. */ status = _txm_module_manager_thread_create(&(module_instance -> txm_module_instance_start_stop_thread), "Module Start Thread", module_instance -> txm_module_instance_shell_entry_function, @@ -158,14 +158,14 @@ UINT status; (UINT) module_instance -> txm_module_instance_start_stop_priority, (UINT) module_instance -> txm_module_instance_start_stop_priority, TXM_MODULE_TIME_SLICE, - TX_DONT_START, + TX_DONT_START, sizeof(TX_THREAD), module_instance); - + /* Determine if the thread create was successful. */ if (status != TX_SUCCESS) { - + /* Delete the callback notification queue. */ _tx_queue_delete(&(module_instance -> txm_module_instance_callback_request_queue)); @@ -173,10 +173,10 @@ UINT status; _tx_mutex_put(&_txm_module_manager_mutex); /* Return the error status. */ - return(status); + return(status); } - /* Create the module callback thread. */ + /* Create the module callback thread. */ status = _txm_module_manager_thread_create(&(module_instance -> txm_module_instance_callback_request_thread), "Module Callback Request Thread", module_instance -> txm_module_instance_shell_entry_function, @@ -187,20 +187,20 @@ UINT status; (UINT) module_instance -> txm_module_instance_callback_priority, (UINT) module_instance -> txm_module_instance_callback_priority, TX_NO_TIME_SLICE, - TX_DONT_START, + TX_DONT_START, sizeof(TX_THREAD), module_instance); - + /* Determine if the thread create was successful. */ if (status != TX_SUCCESS) { /* Terminate the start thread. */ _tx_thread_terminate(&(module_instance -> txm_module_instance_start_stop_thread)); - + /* Delete the start thread. */ _tx_thread_delete(&(module_instance -> txm_module_instance_start_stop_thread)); - + /* Delete the callback notification queue. */ _tx_queue_delete(&(module_instance -> txm_module_instance_callback_request_queue)); @@ -208,7 +208,7 @@ UINT status; _tx_mutex_put(&_txm_module_manager_mutex); /* Return the error status. */ - return(status); + return(status); } diff --git a/common_modules/module_manager/src/txm_module_manager_thread_notify_trampoline.c b/common_modules/module_manager/src/txm_module_manager_thread_notify_trampoline.c index d70ff718..daff01ce 100644 --- a/common_modules/module_manager/src/txm_module_manager_thread_notify_trampoline.c +++ b/common_modules/module_manager/src/txm_module_manager_thread_notify_trampoline.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -28,43 +28,43 @@ #include "txm_module.h" #ifndef TX_DISABLE_NOTIFY_CALLBACKS -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_thread_notify_trampoline PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_thread_notify_trampoline PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes the thread entry/exit notification call */ -/* from ThreadX. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Thread pointer */ -/* type Entry or exit type */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_manager_callback_request Send module callback request */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the thread entry/exit notification call */ +/* from ThreadX. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Thread pointer */ +/* type Entry or exit type */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_callback_request Send module callback request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _txm_module_manager_thread_notify_trampoline(TX_THREAD *thread_ptr, UINT type) @@ -77,9 +77,9 @@ TXM_MODULE_CALLBACK_MESSAGE callback_message; TX_QUEUE *module_callback_queue; TXM_MODULE_THREAD_ENTRY_INFO *thread_info; - + /* We now know the callback is for a module. */ - + /* Disable interrupts. */ TX_DISABLE @@ -97,9 +97,9 @@ TXM_MODULE_THREAD_ENTRY_INFO *thread_info; if ((module_instance) && (module_instance -> txm_module_instance_id == TXM_MODULE_ID) && (module_instance -> txm_module_instance_state == TXM_MODULE_STARTED)) { - - /* Yes, the module is still valid. */ - + + /* Yes, the module is still valid. */ + /* Pickup the module's callback message queue. */ module_callback_queue = &(module_instance -> txm_module_instance_callback_request_queue); @@ -117,19 +117,19 @@ TXM_MODULE_THREAD_ENTRY_INFO *thread_info; callback_message.txm_module_callback_message_param_8 = 0; callback_message.txm_module_callback_message_reserved1 = 0; callback_message.txm_module_callback_message_reserved2 = 0; - + /* Restore interrupts. */ TX_RESTORE - - /* Call the general processing that will place the callback on the + + /* Call the general processing that will place the callback on the module's callback request queue. */ _txm_module_manager_callback_request(module_callback_queue, &callback_message); } else { - + /* Module no longer valid. */ - + /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; @@ -139,15 +139,15 @@ TXM_MODULE_THREAD_ENTRY_INFO *thread_info; } else { - + /* Thread pointer is not valid. */ - + /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; /* Restore interrupts. */ TX_RESTORE } -} +} #endif - + diff --git a/common_modules/module_manager/src/txm_module_manager_thread_reset.c b/common_modules/module_manager/src/txm_module_manager_thread_reset.c index 0fd0b2af..1a9d2ac8 100644 --- a/common_modules/module_manager/src/txm_module_manager_thread_reset.c +++ b/common_modules/module_manager/src/txm_module_manager_thread_reset.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -27,45 +27,45 @@ #include "tx_thread.h" #include "txm_module.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_thread_reset PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_thread_reset PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function prepares the thread to run again from the entry */ -/* point specified during thread creation. The application must */ -/* call tx_thread_resume after this call completes for the thread */ -/* to actually run. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread to reset */ -/* */ -/* OUTPUT */ -/* */ -/* status Service return status */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_manager_thread_stack_build Build initial thread */ -/* stack */ -/* */ -/* CALLED BY */ -/* */ -/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function prepares the thread to run again from the entry */ +/* point specified during thread creation. The application must */ +/* call tx_thread_resume after this call completes for the thread */ +/* to actually run. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread to reset */ +/* */ +/* OUTPUT */ +/* */ +/* status Service return status */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_thread_stack_build Build initial thread */ +/* stack */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_thread_reset(TX_THREAD *thread_ptr) @@ -101,7 +101,7 @@ TXM_MODULE_THREAD_ENTRY_INFO *thread_entry_info; { /* Check for proper status of this thread to reset. */ - if (thread_ptr -> tx_thread_state != TX_COMPLETED) + if (thread_ptr -> tx_thread_state != TX_COMPLETED) { /* Now check for terminated state. */ @@ -139,18 +139,18 @@ TXM_MODULE_THREAD_ENTRY_INFO *thread_entry_info; #endif /* Setup pointer to the thread entry information structure, which will live at the top of each - module thread's stack. This will allow the module thread entry function to avoid direct + module thread's stack. This will allow the module thread entry function to avoid direct access to the actual thread control block. */ thread_entry_info = (TXM_MODULE_THREAD_ENTRY_INFO *) (((UCHAR *) thread_ptr -> tx_thread_stack_end) + (2*sizeof(ULONG)) + 1); thread_entry_info = (TXM_MODULE_THREAD_ENTRY_INFO *) (((ALIGN_TYPE)(thread_entry_info)) & (~0x3)); - + /* Place the thread entry information pointer in the thread control block so it can be picked up in the following stack build function. This is supplied to the module's shell entry function to avoid direct access to the actual thread control block. Note that this is overwritten with the actual stack pointer at the end of stack build. */ thread_ptr -> tx_thread_stack_ptr = (VOID *) thread_entry_info; - - /* Call the target specific stack frame building routine to build the + + /* Call the target specific stack frame building routine to build the thread's initial stack and to setup the actual stack pointer in the control block. */ _txm_module_manager_thread_stack_build(thread_ptr, module_instance -> txm_module_instance_shell_entry_function); diff --git a/common_modules/module_manager/src/txm_module_manager_timer_notify_trampoline.c b/common_modules/module_manager/src/txm_module_manager_timer_notify_trampoline.c index 9bcdd5d9..a7566c92 100644 --- a/common_modules/module_manager/src/txm_module_manager_timer_notify_trampoline.c +++ b/common_modules/module_manager/src/txm_module_manager_timer_notify_trampoline.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -27,43 +27,43 @@ #include "tx_thread.h" #include "tx_timer.h" #include "txm_module.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_timer_notify_trampoline PORTABLE C */ + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_timer_notify_trampoline PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function processes the timer expirations from ThreadX. */ -/* */ -/* INPUT */ -/* */ -/* id Timer ID */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _txm_module_manager_callback_request Send module callback request */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the timer expirations from ThreadX. */ +/* */ +/* INPUT */ +/* */ +/* id Timer ID */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_callback_request Send module callback request */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ VOID _txm_module_manager_timer_notify_trampoline(ULONG id) @@ -77,20 +77,20 @@ TX_QUEUE *module_callback_queue; TX_TIMER *timer_ptr; CHAR *internal_ptr; - + /* We now know the callback is for a module. */ - + /* Disable interrupts. */ TX_DISABLE - - /* Our expired timer pointer points to the internal timer, + + /* Our expired timer pointer points to the internal timer, * we need to get to the full timer pointer. */ /* Pickup the current internal timer pointer. */ - internal_ptr = (CHAR *) _tx_timer_expired_timer_ptr; - + internal_ptr = (CHAR *) _tx_timer_expired_timer_ptr; + /* Get the timer pointer from the internal pointer. */ TX_USER_TIMER_POINTER_GET((TX_TIMER_INTERNAL *) internal_ptr, timer_ptr); - + /* Pickup the module instance pointer. */ module_instance = (TXM_MODULE_INSTANCE *) timer_ptr -> tx_timer_module_instance; @@ -98,9 +98,9 @@ CHAR *internal_ptr; if ((module_instance) && (module_instance -> txm_module_instance_id == TXM_MODULE_ID) && (module_instance -> txm_module_instance_state == TXM_MODULE_STARTED)) { - - /* Yes, the module is still valid. */ - + + /* Yes, the module is still valid. */ + /* Pickup the module's callback message queue. */ module_callback_queue = &(module_instance -> txm_module_instance_callback_request_queue); @@ -118,24 +118,24 @@ CHAR *internal_ptr; callback_message.txm_module_callback_message_param_8 = 0; callback_message.txm_module_callback_message_reserved1 = 0; callback_message.txm_module_callback_message_reserved2 = 0; - + /* Restore interrupts. */ TX_RESTORE - - /* Call the general processing that will place the callback on the + + /* Call the general processing that will place the callback on the module's callback request queue. */ _txm_module_manager_callback_request(module_callback_queue, &callback_message); } else { - + /* Module no longer valid. */ /* Error, increment the error counter and return. */ _txm_module_manager_callback_error_count++; - + /* Restore interrupts. */ TX_RESTORE } -} - +} + diff --git a/common_modules/module_manager/src/txm_module_manager_unload.c b/common_modules/module_manager/src/txm_module_manager_unload.c index 4f83f726..fd5f88af 100644 --- a/common_modules/module_manager/src/txm_module_manager_unload.c +++ b/common_modules/module_manager/src/txm_module_manager_unload.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ #define TX_SOURCE_CODE @@ -30,50 +30,50 @@ #include "txm_module.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_unload PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_unload PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function unloads a previously loaded module. */ -/* */ -/* INPUT */ -/* */ -/* module_instance Module instance pointer */ -/* */ -/* OUTPUT */ -/* */ -/* status Completion status */ -/* */ -/* CALLS */ -/* */ -/* _tx_byte_release Release data area */ -/* _tx_mutex_get Get protection mutex */ -/* _tx_mutex_put Release protection mutex */ -/* */ -/* CALLED BY */ -/* */ -/* Application code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function unloads a previously loaded module. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_byte_release Release data area */ +/* _tx_mutex_get Get protection mutex */ +/* _tx_mutex_put Release protection mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_unload(TXM_MODULE_INSTANCE *module_instance) { TX_INTERRUPT_SAVE_AREA - + TXM_MODULE_INSTANCE *next_module, *previous_module; CHAR *memory_ptr; @@ -81,11 +81,11 @@ CHAR *memory_ptr; /* Check for interrupt call. */ if (TX_THREAD_GET_SYSTEM_STATE() != 0) { - + /* Now, make sure the call is from an interrupt and not initialization. */ if (TX_THREAD_GET_SYSTEM_STATE() < TX_INITIALIZE_IN_PROGRESS) { - + /* Invalid caller of this function, return appropriate error code. */ return(TX_CALLER_ERROR); } @@ -94,15 +94,15 @@ CHAR *memory_ptr; /* Determine if the module manager has not been initialized yet. */ if (_txm_module_manager_ready != TX_TRUE) { - + /* Module manager has not been initialized. */ - return(TX_NOT_AVAILABLE); + return(TX_NOT_AVAILABLE); } /* Determine if the module is valid. */ if (module_instance == TX_NULL) { - + /* Invalid module pointer. */ return(TX_PTR_ERROR); } @@ -124,7 +124,7 @@ CHAR *memory_ptr; /* Determine if the module instance is in the state. */ if ((module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) && (module_instance -> txm_module_instance_state != TXM_MODULE_STOPPED)) { - + /* Release the protection mutex. */ _tx_mutex_put(&_txm_module_manager_mutex); @@ -141,14 +141,14 @@ CHAR *memory_ptr; /* Determine if there was memory allocated for the code. */ if (module_instance -> txm_module_instance_code_allocation_ptr) { - + /* Yes, release the module's code memory. */ memory_ptr = module_instance -> txm_module_instance_code_allocation_ptr; /* Release the module's data memory. */ _tx_byte_release(memory_ptr); } - + /* Temporarily disable interrupts. */ TX_DISABLE @@ -158,7 +158,7 @@ CHAR *memory_ptr; /* Call port-specific unload function. */ TXM_MODULE_MANAGER_MODULE_UNLOAD(module_instance); - + /* Remove the module from the linked list of loaded modules. */ /* See if the module is the only one on the list. */ @@ -180,9 +180,9 @@ CHAR *memory_ptr; /* See if we have to update the created list head pointer. */ if (_txm_module_manager_loaded_list_ptr == module_instance) { - + /* Yes, move the head pointer to the next link. */ - _txm_module_manager_loaded_list_ptr = next_module; + _txm_module_manager_loaded_list_ptr = next_module; } } diff --git a/common_modules/module_manager/src/txm_module_manager_util.c b/common_modules/module_manager/src/txm_module_manager_util.c index 8511fce6..bd6c79eb 100644 --- a/common_modules/module_manager/src/txm_module_manager_util.c +++ b/common_modules/module_manager/src/txm_module_manager_util.c @@ -10,15 +10,15 @@ /**************************************************************************/ -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Module Manager */ -/** */ -/**************************************************************************/ -/**************************************************************************/ +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ /* Include necessary system files. */ @@ -29,115 +29,115 @@ #include "txm_module_manager_util.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_object_memory_check PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_object_memory_check PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks if the object is inside a module's object pool */ -/* or, if it's a privileged module, inside the module's data area. */ -/* */ -/* INPUT */ -/* */ -/* module_instance Module instance that the object */ -/* belongs to */ -/* object_ptr Pointer to object to check */ -/* object_size Size of the object to check */ -/* */ -/* OUTPUT */ -/* */ -/* status Whether the object resides in a */ -/* valid location */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if the object is inside a module's object pool */ +/* or, if it's a privileged module, inside the module's data area. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance that the object */ +/* belongs to */ +/* object_ptr Pointer to object to check */ +/* object_size Size of the object to check */ +/* */ +/* OUTPUT */ +/* */ +/* status Whether the object resides in a */ +/* valid location */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_memory_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, ULONG object_size) { - + /* Is the object pointer from the module manager's object pool? */ - if ((_txm_module_manager_object_pool_created == TX_TRUE) && - (object_ptr >= (ALIGN_TYPE) _txm_module_manager_object_pool.tx_byte_pool_start) && + if ((_txm_module_manager_object_pool_created == TX_TRUE) && + (object_ptr >= (ALIGN_TYPE) _txm_module_manager_object_pool.tx_byte_pool_start) && ((object_ptr+object_size) <= (ALIGN_TYPE) (_txm_module_manager_object_pool.tx_byte_pool_start + _txm_module_manager_object_pool.tx_byte_pool_size))) { /* Object is from manager object pool. */ return(TX_SUCCESS); } - + /* If memory protection is not required, check if object is in module data. */ else if (!(module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION)) { - if ((object_ptr >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && + if ((object_ptr >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && ((object_ptr+object_size) <= (ALIGN_TYPE) module_instance -> txm_module_instance_data_end)) { /* Object is from the local module memory. */ return(TX_SUCCESS); } } - + /* Object is from invalid memory. */ return(TXM_MODULE_INVALID_MEMORY); - + } -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_created_object_check PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_created_object_check PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ +/* DESCRIPTION */ +/* */ /* This functions checks if the specified object was created by the */ -/* specified module */ -/* */ -/* INPUT */ -/* */ -/* module_instance The module instance to check */ -/* object_ptr The object to check */ -/* */ -/* OUTPUT */ -/* */ -/* status Whether the module created the */ -/* object */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* txm_module_manager*_stop Module manager stop functions */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* specified module */ +/* */ +/* INPUT */ +/* */ +/* module_instance The module instance to check */ +/* object_ptr The object to check */ +/* */ +/* OUTPUT */ +/* */ +/* status Whether the module created the */ +/* object */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* txm_module_manager*_stop Module manager stop functions */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UCHAR _txm_module_manager_created_object_check(TXM_MODULE_INSTANCE *module_instance, VOID *object_ptr) @@ -179,51 +179,51 @@ TXM_MODULE_ALLOCATED_OBJECT *allocated_object_ptr; } -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_object_size_check PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_object_size_check PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function checks if the specified object's size matches what is */ -/* inside the object pool. */ -/* */ -/* INPUT */ -/* */ -/* object_ptr Pointer to object to check */ -/* object_size Size of the object to check */ -/* */ -/* OUTPUT */ -/* */ -/* status Whether the object's size matches */ -/* what's inside the object pool */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if the specified object's size matches what is */ +/* inside the object pool. */ +/* */ +/* INPUT */ +/* */ +/* object_ptr Pointer to object to check */ +/* object_size Size of the object to check */ +/* */ +/* OUTPUT */ +/* */ +/* status Whether the object's size matches */ +/* what's inside the object pool */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_kernel_dispatch Kernel dispatch function */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_size_check(ALIGN_TYPE object_ptr, ULONG object_size) { TXM_MODULE_ALLOCATED_OBJECT *module_allocated_object_ptr; UINT return_value; - + /* Pickup the allocated object pointer. */ module_allocated_object_ptr = ((TXM_MODULE_ALLOCATED_OBJECT *) object_ptr) - 1; @@ -232,50 +232,50 @@ UINT return_value; return_value = TX_SUCCESS; else return_value = TXM_MODULE_INVALID_MEMORY; - + return(return_value); } -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_name_compare PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_name_compare PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function compares the specified object names. */ -/* */ -/* INPUT */ -/* */ -/* search_name String pointer to the object's */ -/* name being searched for */ -/* search_name_length Length of search_name */ -/* object_name String pointer to an object's name*/ -/* to compare the search name to */ -/* */ -/* OUTPUT */ -/* */ -/* status Whether the names are equal */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* *_object_pointer_get Kernel dispatch function */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function compares the specified object names. */ +/* */ +/* INPUT */ +/* */ +/* search_name String pointer to the object's */ +/* name being searched for */ +/* search_name_length Length of search_name */ +/* object_name String pointer to an object's name*/ +/* to compare the search name to */ +/* */ +/* OUTPUT */ +/* */ +/* status Whether the names are equal */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* *_object_pointer_get Kernel dispatch function */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_object_name_compare(CHAR *search_name, UINT search_name_length, CHAR *object_name) @@ -337,48 +337,48 @@ CHAR object_name_char; } -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _txm_module_manager_util_code_allocation_size_and_alignment_get */ -/* PORTABLE C */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_util_code_allocation_size_and_alignment_get */ +/* PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ -/* DESCRIPTION */ -/* */ -/* This function returns the required alignment and allocation size */ -/* for a module's code area. */ -/* */ -/* INPUT */ -/* */ -/* module_preamble Preamble of module to return code */ -/* values for */ -/* code_alignment_dest Address to return code alignment */ -/* code_allocation_size_desk Address to return code allocation */ -/* size */ -/* */ -/* OUTPUT */ -/* */ -/* status Success if no math overflow */ -/* occurred during calculation */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* txm_module_manager_*_load Module load functions */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the required alignment and allocation size */ +/* for a module's code area. */ +/* */ +/* INPUT */ +/* */ +/* module_preamble Preamble of module to return code */ +/* values for */ +/* code_alignment_dest Address to return code alignment */ +/* code_allocation_size_desk Address to return code allocation */ +/* size */ +/* */ +/* OUTPUT */ +/* */ +/* status Success if no math overflow */ +/* occurred during calculation */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* txm_module_manager_*_load Module load functions */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ UINT _txm_module_manager_util_code_allocation_size_and_alignment_get(TXM_MODULE_PREAMBLE *module_preamble, diff --git a/common_smp/inc/tx_api.h b/common_smp/inc/tx_api.h index 5910c803..9591aee9 100644 --- a/common_smp/inc/tx_api.h +++ b/common_smp/inc/tx_api.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* tx_api.h PORTABLE SMP */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -67,6 +67,10 @@ /* 10-15-2021 Yuxin Zhou Modified comment(s), */ /* update patch number, */ /* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), */ +/* add unused parameter macro, */ +/* update patch number, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -84,6 +88,10 @@ extern "C" { #endif +/* Disable warning of parameter not used. */ +#ifndef TX_PARAMETER_NOT_USED +#define TX_PARAMETER_NOT_USED(p) ((void)(p)) +#endif /* TX_PARAMETER_NOT_USED */ /* Include the port-specific data type file. */ @@ -115,7 +123,7 @@ extern "C" { #define AZURE_RTOS_THREADX #define THREADX_MAJOR_VERSION 6 #define THREADX_MINOR_VERSION 1 -#define THREADX_PATCH_VERSION 9 +#define THREADX_PATCH_VERSION 10 /* Define the following symbol for backward compatibility */ #define EL_PRODUCT_THREADX diff --git a/ports/arc_em/metaware/example_build/sample_threadx/tx_initialize_low_level.s b/ports/arc_em/metaware/example_build/sample_threadx/tx_initialize_low_level.s index 69d42895..a7406360 100644 --- a/ports/arc_em/metaware/example_build/sample_threadx/tx_initialize_low_level.s +++ b/ports/arc_em/metaware/example_build/sample_threadx/tx_initialize_low_level.s @@ -60,7 +60,7 @@ _tx_system_stack_base_address: ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_initialize_low_level ARCv2_EM/MetaWare */ -;/* 6.1.9 */ +;/* 6.1.10 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -97,10 +97,16 @@ _tx_system_stack_base_address: ;/* 10-15-2021 Andres Mlinar Modified comment(s), optimized*/ ;/* system stack usage, */ ;/* resulting in version 6.1.9 */ +;/* 01-31-2022 Andres Mlinar Modified comments(s), */ +;/* initialize interrupts right */ +;/* before enabling the task */ +;/* scheduler, */ +;/* resulting in version 6.1.10 */ ;/* */ ;/**************************************************************************/ ;VOID _tx_initialize_low_level(VOID) ;{ + .align 4 .global _tx_initialize_low_level .type _tx_initialize_low_level, @function _tx_initialize_low_level: @@ -131,6 +137,18 @@ _tx_initialize_low_level: ; st r0, [gp, _tx_initialize_unused_memory@sda] ; +; /* Done, return to caller. */ +; + j_s.d [blink] ; Return to caller + nop +;} +; +;VOID _tx_initialize_start_interrupts(VOID) +;{ + .align 4 + .global _tx_initialize_start_interrupts + .type _tx_initialize_start_interrupts, @function +_tx_initialize_start_interrupts: ; ; /* Setup Timer 0 for periodic interrupts at interrupt vector 16. */ ; diff --git a/ports/arc_em/metaware/inc/tx_port.h b/ports/arc_em/metaware/inc/tx_port.h index fd1ef746..5cc0be6b 100644 --- a/ports/arc_em/metaware/inc/tx_port.h +++ b/ports/arc_em/metaware/inc/tx_port.h @@ -48,9 +48,14 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 04-02-2021 Bhupendra Naphade Modified comment(s), updated */ /* macro definition, */ /* resulting in version 6.1.6 */ +/* 01-31-2022 Andres Mlinar Modified comments(s), */ +/* initialize interrupts right */ +/* before enabling the task */ +/* scheduler, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -184,6 +189,12 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_INLINE_INITIALIZATION #endif +/* Define the ARC-specific initialization code that is expanded in the generic source. */ + +void _tx_initialize_start_interrupts(void); + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION _tx_initialize_start_interrupts(); + /* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack @@ -311,7 +322,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARCv2_EM/MetaWare Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARCv2_EM/MetaWare Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports/arc_em/metaware/test_regression/threadx_regression/.cproject b/ports/arc_em/metaware/test_regression/threadx_regression/.cproject deleted file mode 100644 index bba84f3c..00000000 --- a/ports/arc_em/metaware/test_regression/threadx_regression/.cproject +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/arc_em/metaware/test_regression/threadx_regression/sample_threadx_regression.cmd b/ports/arc_em/metaware/test_regression/threadx_regression/sample_threadx_regression.cmd deleted file mode 100644 index 78dc1f6e..00000000 --- a/ports/arc_em/metaware/test_regression/threadx_regression/sample_threadx_regression.cmd +++ /dev/null @@ -1,55 +0,0 @@ -// -// This is the linker script example (SRV3-style). -// (c) Synopsys, 2013 -// -// - -//number of exceptions and interrupts -NUMBER_OF_EXCEPTIONS = 16;//it is fixed (16) -NUMBER_OF_INTERRUPTS = 5;//depends on HW configuration - -//define Interrupt Vector Table size -IVT_SIZE_ITEMS = (NUMBER_OF_EXCEPTIONS + NUMBER_OF_INTERRUPTS);//the total IVT size (in "items") -IVT_SIZE_BYTES = IVT_SIZE_ITEMS * 4;//in bytes - -//define ICCM and DCCM locations -MEMORY { - ICCM: ORIGIN = 0x00000000, LENGTH = 128K - DCCM: ORIGIN = 0x80000000, LENGTH = 128K -} - -//define sections and groups -SECTIONS { - GROUP: { - .ivt (TEXT) : # Interrupt table - { - ___ivt1 = .; - * (.ivt) - ___ivt2 = .; - // Make the IVT at least IVT_SIZE_BYTES - . += (___ivt2 - ___ivt1 < IVT_SIZE_BYTES) ? (IVT_SIZE_BYTES - (___ivt2 - ___ivt1)) : 0; - } - .ivh (TEXT) : // Interrupt handlers - - //TEXT sections - .text? : { *('.text$crt*') } - * (TEXT): {} - //Literals - * (LIT): {} - } > ICCM - - GROUP: { - //data sections - .sdata?: {} - .sbss?: {} - *(DATA): {} - *(BSS): {} - //stack - .stack_top: {} - .stack ALIGN(4) SIZE(DEFINED _STACKSIZE?_STACKSIZE:4096): {} - .stack_base: {} - //heap (empty) - .heap? ALIGN(4) SIZE(DEFINED _HEAPSIZE?_HEAPSIZE:0): {} - .free_memory: {} - } > DCCM - } diff --git a/ports/arc_em/metaware/test_regression/threadx_regression/tx_initialize_low_level.s b/ports/arc_em/metaware/test_regression/threadx_regression/tx_initialize_low_level.s deleted file mode 100644 index bea01020..00000000 --- a/ports/arc_em/metaware/test_regression/threadx_regression/tx_initialize_low_level.s +++ /dev/null @@ -1,360 +0,0 @@ -;/**************************************************************************/ -;/* */ -;/* Copyright (c) Microsoft Corporation. All rights reserved. */ -;/* */ -;/* This software is licensed under the Microsoft Software License */ -;/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -;/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -;/* and in the root directory of this software. */ -;/* */ -;/**************************************************************************/ - -;/**************************************************************************/ -;/**************************************************************************/ -;/** */ -;/** ThreadX Component */ -;/** */ -;/** Initialize */ -;/** */ -;/**************************************************************************/ -;/**************************************************************************/ - - .equ IRQ_SELECT, 0x40B - .equ KSTACK_TOP, 0x264 - .equ KSTACK_BASE, 0x265 - .equ STATUS32_SC, 0x4000 - -; -; /* Define section for placement after all linker allocated RAM memory. This -; is used to calculate the first free address that is passed to -; tx_appication_define, soley for the ThreadX application's use. */ -; - .section ".free_memory","aw" - .align 4 - .global _tx_first_free_address -_tx_first_free_address: - .space 4 -; -; /* Define section for placement before the main stack area for setting -; up the STACK_TOP address for hardware stack checking. */ -; - .section ".stack_top","aw" - .align 4 - .global _tx_system_stack_top_address -_tx_system_stack_top_address: - .space 4 -; -; /* Define section for placement after the main stack area for setting -; up the STACK_BASE address for hardware stack checking. */ -; - .section ".stack_base","aw" - .align 4 - .global _tx_system_stack_base_address -_tx_system_stack_base_address: - .space 4 -; -; - .text -;/**************************************************************************/ -;/* */ -;/* FUNCTION RELEASE */ -;/* */ -;/* _tx_initialize_low_level ARCv2_EM/MetaWare */ -;/* 6.x */ -;/* AUTHOR */ -;/* */ -;/* William E. Lamie, 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 */ -;/* */ -;/* RELEASE HISTORY */ -;/* */ -;/* DATE NAME DESCRIPTION */ -;/* */ -;/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -;/* */ -;/**************************************************************************/ -;VOID _tx_initialize_low_level(VOID) -;{ - .global _tx_initialize_low_level - .type _tx_initialize_low_level, @function -_tx_initialize_low_level: - - .ifdef TX_ENABLE_HW_STACK_CHECKING - mov r0, _tx_system_stack_top_address ; Pickup top of system stack (lowest memory address) - sr r0, [KSTACK_TOP] ; Setup KSTACK_TOP - mov r0, _tx_system_stack_base_address ; Pickup base of system stack (highest memory address) - sr r0, [KSTACK_BASE] ; Setup KSTACK_BASE - lr r0, [status32] ; Pickup current STATUS32 - or r0, r0, STATUS32_SC ; Or in hardware stack checking enable bit (SC) - kflag r0 ; Enable hardware stack checking - .endif -; -; /* Save the system stack pointer. */ -; _tx_thread_system_stack_ptr = (VOID_PTR) (sp); -; - mov r0, _estack ; Pickup the end of stack address - st r0, [gp, _tx_thread_system_stack_ptr@sda] ; Save system stack pointer -; -; -; /* Pickup the first available memory address. */ -; - mov r0, _tx_first_free_address ; Pickup first free memory address -; -; /* Save the first available memory address. */ -; _tx_initialize_unused_memory = (VOID_PTR) _end; -; - st r0, [gp, _tx_initialize_unused_memory@sda] -; -; -; /* Setup Timer 0 for periodic interrupts at interrupt vector 16. */ -; - mov r0, 0 ; Disable additional ISR reg saving/restoring - sr r0, [AUX_IRQ_CTRL] ; - - mov r0, 16 ; Select timer 0 - sr r0, [IRQ_SELECT] ; - mov r0, 15 ; Set timer 0 to priority 15 - sr r0, [IRQ_PRIORITY] ; - mov r0, 1 ; Enable this interrupt - sr r0, [IRQ_ENABLE] ; - mov r0, 0x10000 ; Setup timer period - sr r0, [LIMIT0] ; - mov r0, 0 ; Clear timer 0 current count - sr r0, [COUNT0] ; - mov r0, 3 ; Enable timer 0 - sr r0, [CONTROL0] ; - - .ifdef TX_TIMER_1_SETUP - mov r0, 17 ; Select timer 1 - sr r0, [IRQ_SELECT] ; - mov r0, 2 ; Set timer 1 to priority 14 - sr r0, [IRQ_PRIORITY] ; - mov r0, 1 ; Enable this interrupt - sr r0, [IRQ_ENABLE] ; - mov r0, 0x10020 ; Setup timer period - sr r0, [LIMIT1] ; - mov r0, 0 ; Clear timer 0 current count - sr r0, [COUNT1] ; - mov r0, 3 ; Enable timer 0 - sr r0, [CONTROL1] ; - .endif -; -; /* Done, return to caller. */ -; - j_s.d [blink] ; Return to caller - nop -;} -; -; -; /* Define default vector table entries. */ -; - .global _tx_memory_error -_tx_memory_error: - flag 1 - nop - nop - nop - b _tx_memory_error - - .global _tx_instruction_error -_tx_instruction_error: - flag 1 - nop - nop - nop - b _tx_instruction_error - - .global _tx_ev_machine_check -_tx_ev_machine_check: - flag 1 - nop - nop - nop - b _tx_ev_machine_check - - .global _tx_ev_tblmiss_inst -_tx_ev_tblmiss_inst: - flag 1 - nop - nop - nop - b _tx_ev_tblmiss_inst - - .global _tx_ev_tblmiss_data -_tx_ev_tblmiss_data: - flag 1 - nop - nop - nop - b _tx_ev_tblmiss_data - - .global _tx_ev_protection_viol -_tx_ev_protection_viol: - flag 1 - nop - nop - nop - b _tx_ev_protection_viol - - .global _tx_ev_privilege_viol -_tx_ev_privilege_viol: - flag 1 - nop - nop - nop - b _tx_ev_privilege_viol - - .global _tx_ev_software_int -_tx_ev_software_int: - flag 1 - nop - nop - nop - b _tx_ev_software_int - - .global _tx_ev_trap -_tx_ev_trap: - flag 1 - nop - nop - nop - b _tx_ev_trap - - .global _tx_ev_extension -_tx_ev_extension: - flag 1 - nop - nop - nop - b _tx_ev_extension - - .global _tx_ev_divide_by_zero -_tx_ev_divide_by_zero: - flag 1 - nop - nop - nop - b _tx_ev_divide_by_zero - - .global _tx_ev_dc_error -_tx_ev_dc_error: - flag 1 - nop - nop - nop - b _tx_ev_dc_error - - .global _tx_ev_maligned -_tx_ev_maligned: - flag 1 - nop - nop - nop - b _tx_ev_maligned - - .global _tx_unsued_0 -_tx_unsued_0: - flag 1 - nop - nop - nop - b _tx_unsued_0 - - .global _tx_unused_1 -_tx_unused_1: - flag 1 - nop - nop - nop - b _tx_unused_1 - - .global _tx_timer_0 -_tx_timer_0: -; -; /* By default, setup Timer 0 as the ThreadX timer interrupt. */ -; - sub sp, sp, 160 ; Allocate an interrupt stack frame - st r0, [sp, 0] ; Save r0 - st r1, [sp, 4] ; Save r1 - st r2, [sp, 8] ; Save r2 - mov r0, 3 - sr r0, [CONTROL0] - - b _tx_timer_interrupt ; Jump to generic ThreadX timer interrupt - ; handler -; flag 1 -; nop -; nop -; nop -; b _tx_timer_0 - - .global _tx_timer_1 -_tx_timer_1: - sub sp, sp, 160 ; Allocate an interrupt stack frame - st blink, [sp, 16] ; Save blink - bl _tx_thread_context_save ; Call context save -; -; /* ISR processing goes here. If the applications wishes to re-enable -; interrupts, the SETI instruction can be used here. Also note that -; register usage in assembly code must be confined to the compiler -; scratch registers. */ -; - mov r0, 3 - sr r0, [CONTROL1] -; - b _tx_thread_context_restore ; Call context restore - -; flag 1 -; nop -; nop -; nop -; b _tx_timer_1 - - .global _tx_undefined_0 -_tx_undefined_0: - flag 1 - nop - nop - nop - b _tx_undefined_0 - - .global _tx_undefined_1 -_tx_undefined_1: - flag 1 - nop - nop - nop - b _tx_undefined_1 - - .global _tx_undefined_2 -_tx_undefined_2: - flag 1 - nop - nop - nop - b _tx_undefined_2 - - .end diff --git a/ports/arc_em/metaware/test_regression/threadx_regression/vectors.s b/ports/arc_em/metaware/test_regression/threadx_regression/vectors.s deleted file mode 100644 index c6cbc893..00000000 --- a/ports/arc_em/metaware/test_regression/threadx_regression/vectors.s +++ /dev/null @@ -1,29 +0,0 @@ - -.file "vectors.s" -.section .ivt,text -;; This directive forces this section to stay resident even if stripped out by the -zpurgetext linker option -.sectflag .ivt,include - -;// handler's name type number name offset in IVT (hex/dec) -.long _start ; exception 0 program entry point offset 0x0 0 -.long _tx_memory_error ; exception 1 memory_error offset 0x4 4 -.long _tx_instruction_error ; exception 2 instruction_error offset 0x8 8 -.long _tx_ev_machine_check ; exception 3 EV_MachineCheck offset 0xC 12 -.long _tx_ev_tblmiss_inst ; exception 4 EV_TLBMissI offset 0x10 16 -.long _tx_ev_tblmiss_data ; exception 5 EV_TLBMissD offset 0x14 20 -.long _tx_ev_protection_viol ; exception 6 EV_ProtV offset 0x18 24 -.long _tx_ev_privilege_viol ; exception 7 EV_PrivilegeV offset 0x1C 28 -.long _tx_ev_software_int ; exception 8 EV_SWI offset 0x20 32 -.long _tx_ev_trap ; exception 9 EV_Trap offset 0x24 36 -.long _tx_ev_extension ; exception 10 EV_Extension offset 0x28 40 -.long _tx_ev_divide_by_zero ; exception 11 EV_DivZero offset 0x2C 44 -.long _tx_ev_dc_error ; exception 12 EV_DCError offset 0x30 48 -.long _tx_ev_maligned ; exception 13 EV_Maligned offset 0x34 52 -.long _tx_unsued_0 ; exception 14 unused offset 0x38 56 -.long _tx_unused_1 ; exception 15 unused offset 0x3C 60 -.long _tx_timer_0 ; IRQ 16 Timer 0 offset 0x40 64 -.long _tx_timer_1 ; IRQ 17 Timer 1 offset 0x44 68 -.long _tx_undefined_0 ; IRQ 18 offset 0x48 72 -.long _tx_undefined_1 ; IRQ 19 offset 0x4C 76 -.long _tx_undefined_2 ; IRQ 20 offset 0x50 80 - diff --git a/ports/arc_em/metaware/test_regression/tx/.cproject b/ports/arc_em/metaware/test_regression/tx/.cproject deleted file mode 100644 index b9202ea5..00000000 --- a/ports/arc_em/metaware/test_regression/tx/.cproject +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/arc_em/metaware/test_regression/tx/tx_user.h b/ports/arc_em/metaware/test_regression/tx/tx_user.h deleted file mode 100644 index 1b5bc378..00000000 --- a/ports/arc_em/metaware/test_regression/tx/tx_user.h +++ /dev/null @@ -1,341 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) Microsoft Corporation. All rights reserved. */ -/* */ -/* This software is licensed under the Microsoft Software License */ -/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -/* and in the root directory of this software. */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** User Specific */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_user.h PORTABLE C */ -/* 6.x */ -/* */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This file contains user defines for configuring ThreadX in specific */ -/* ways. This file will have an effect only if the application and */ -/* ThreadX library are built with TX_INCLUDE_USER_DEFINE_FILE defined. */ -/* Note that all the defines in this file may also be made on the */ -/* command line when building ThreadX library and application objects. */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 05-19-2020 William E. Lamie Initial Version 6.0 */ -/* 09-30-2020 Yuxin Zhou Modified comment(s), */ -/* resulting in version 6.1 */ -/* xx-xx-xxxx Scott Larson Modified comment(s), */ -/* added option to remove */ -/* FileX pointer, */ -/* resulting in version 6.x */ -/* */ -/**************************************************************************/ - -#ifndef TX_USER_H -#define TX_USER_H - - -#define TX_REGRESSION_TEST -#define TEST_STACK_SIZE_PRINTF (4096) - - -/* Define automated coverage test extensions... These are required for the - ThreadX regression test. */ - -typedef unsigned int TEST_FLAG; -extern TEST_FLAG threadx_byte_allocate_loop_test; -extern TEST_FLAG threadx_byte_release_loop_test; -extern TEST_FLAG threadx_mutex_suspension_put_test; -extern TEST_FLAG threadx_mutex_suspension_priority_test; -#ifndef TX_TIMER_PROCESS_IN_ISR -extern TEST_FLAG threadx_delete_timer_thread; -#endif - -extern void abort_and_resume_byte_allocating_thread(void); -extern void abort_all_threads_suspended_on_mutex(void); -extern void suspend_lowest_priority(void); -#ifndef TX_TIMER_PROCESS_IN_ISR -extern void delete_timer_thread(void); -#endif -extern TEST_FLAG test_stack_analyze_flag; -extern TEST_FLAG test_initialize_flag; -extern TEST_FLAG test_forced_mutex_timeout; - - -#ifdef TX_REGRESSION_TEST - -/* Define extension macros for automated coverage tests. */ - - -#define TX_BYTE_ALLOCATE_EXTENSION if (threadx_byte_allocate_loop_test == ((TEST_FLAG) 1)) \ - { \ - pool_ptr -> tx_byte_pool_owner = TX_NULL; \ - threadx_byte_allocate_loop_test = ((TEST_FLAG) 0); \ - } - -#define TX_BYTE_RELEASE_EXTENSION if (threadx_byte_release_loop_test == ((TEST_FLAG) 1)) \ - { \ - threadx_byte_release_loop_test = ((TEST_FLAG) 0); \ - abort_and_resume_byte_allocating_thread(); \ - } - -#define TX_MUTEX_PUT_EXTENSION_1 if (threadx_mutex_suspension_put_test == ((TEST_FLAG) 1)) \ - { \ - threadx_mutex_suspension_put_test = ((TEST_FLAG) 0); \ - abort_all_threads_suspended_on_mutex(); \ - } - - -#define TX_MUTEX_PUT_EXTENSION_2 if (test_forced_mutex_timeout == ((TEST_FLAG) 1)) \ - { \ - test_forced_mutex_timeout = ((TEST_FLAG) 0); \ - _tx_thread_wait_abort(mutex_ptr -> tx_mutex_suspension_list); \ - } - - -#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION if (threadx_mutex_suspension_priority_test == ((TEST_FLAG) 1)) \ - { \ - threadx_mutex_suspension_priority_test = ((TEST_FLAG) 0); \ - suspend_lowest_priority(); \ - } - -#endif /* TX_REGRESSION_TEST */ - - -/* Define various build options for the ThreadX port. The application should either make changes - here by commenting or un-commenting the conditional compilation defined OR supply the defines - though the compiler's equivalent of the -D option. - - For maximum speed, the following should be defined: - - TX_MAX_PRIORITIES 32 - TX_DISABLE_PREEMPTION_THRESHOLD - TX_DISABLE_REDUNDANT_CLEARING - TX_DISABLE_NOTIFY_CALLBACKS - TX_NOT_INTERRUPTABLE - TX_TIMER_PROCESS_IN_ISR - TX_REACTIVATE_INLINE - TX_DISABLE_STACK_FILLING - TX_INLINE_THREAD_RESUME_SUSPEND - - For minimum size, the following should be defined: - - TX_MAX_PRIORITIES 32 - TX_DISABLE_PREEMPTION_THRESHOLD - TX_DISABLE_REDUNDANT_CLEARING - TX_DISABLE_NOTIFY_CALLBACKS - TX_NO_FILEX_POINTER - TX_NOT_INTERRUPTABLE - TX_TIMER_PROCESS_IN_ISR - - Of course, many of these defines reduce functionality and/or change the behavior of the - system in ways that may not be worth the trade-off. For example, the TX_TIMER_PROCESS_IN_ISR - results in faster and smaller code, however, it increases the amount of processing in the ISR. - In addition, some services that are available in timers are not available from ISRs and will - therefore return an error if this option is used. This may or may not be desirable for a - given application. */ - - -/* Override various options with default values already assigned in tx_port.h. Please also refer - to tx_port.h for descriptions on each of these options. */ - -/* -#define TX_MAX_PRIORITIES 32 -#define TX_MINIMUM_STACK ???? -#define TX_THREAD_USER_EXTENSION ???? -#define TX_TIMER_THREAD_STACK_SIZE ???? -#define TX_TIMER_THREAD_PRIORITY ???? -*/ - -/* Determine if there is a FileX pointer in the thread control block. - By default, the pointer is there for legacy/backwards compatibility. - The pointer must also be there for applications using FileX. - Define this to save space in the thread control block. -*/ - -/* -#define TX_NO_FILEX_POINTER -*/ - -/* Determine if timer expirations (application timers, timeouts, and tx_thread_sleep calls - should be processed within the a system timer thread or directly in the timer ISR. - By default, the timer thread is used. When the following is defined, the timer expiration - processing is done directly from the timer ISR, thereby eliminating the timer thread control - block, stack, and context switching to activate it. */ - -/* -#define TX_TIMER_PROCESS_IN_ISR -*/ - -/* Determine if in-line timer reactivation should be used within the timer expiration processing. - By default, this is disabled and a function call is used. When the following is defined, - reactivating is performed in-line resulting in faster timer processing but slightly larger - code size. */ - -/* -#define TX_REACTIVATE_INLINE -*/ - -/* Determine is stack filling is enabled. By default, ThreadX stack filling is enabled, - which places an 0xEF pattern in each byte of each thread's stack. This is used by - debuggers with ThreadX-awareness and by the ThreadX run-time stack checking feature. */ - -/* -#define TX_DISABLE_STACK_FILLING -*/ - -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is - disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack - checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING - define is negated, thereby forcing the stack fill which is necessary for the stack checking - logic. */ - -/* -#define TX_ENABLE_STACK_CHECKING -*/ - -/* Determine if preemption-threshold should be disabled. By default, preemption-threshold is - enabled. If the application does not use preemption-threshold, it may be disabled to reduce - code size and improve performance. */ - -/* -#define TX_DISABLE_PREEMPTION_THRESHOLD -*/ - -/* Determine if global ThreadX variables should be cleared. If the compiler startup code clears - the .bss section prior to ThreadX running, the define can be used to eliminate unnecessary - clearing of ThreadX global variables. */ - -/* -#define TX_DISABLE_REDUNDANT_CLEARING -*/ - -/* Determine if no timer processing is required. This option will help eliminate the timer - processing when not needed. The user will also have to comment out the call to - tx_timer_interrupt, which is typically made from assembly language in - tx_initialize_low_level. Note: if TX_NO_TIMER is used, the define TX_TIMER_PROCESS_IN_ISR - must also be used and tx_timer_initialize must be removed from ThreadX library. */ - -/* -#define TX_NO_TIMER -#ifndef TX_TIMER_PROCESS_IN_ISR -#define TX_TIMER_PROCESS_IN_ISR -#endif -*/ - -/* Determine if the notify callback option should be disabled. By default, notify callbacks are - enabled. If the application does not use notify callbacks, they may be disabled to reduce - code size and improve performance. */ - -/* -#define TX_DISABLE_NOTIFY_CALLBACKS -*/ - - -/* Determine if the tx_thread_resume and tx_thread_suspend services should have their internal - code in-line. This results in a larger image, but improves the performance of the thread - resume and suspend services. */ - -/* -#define TX_INLINE_THREAD_RESUME_SUSPEND -*/ - - -/* Determine if the internal ThreadX code is non-interruptable. This results in smaller code - size and less processing overhead, but increases the interrupt lockout time. */ - -/* -#define TX_NOT_INTERRUPTABLE -*/ - - -/* Determine if the trace event logging code should be enabled. This causes slight increases in - code size and overhead, but provides the ability to generate system trace information which - is available for viewing in TraceX. */ - -/* -#define TX_ENABLE_EVENT_TRACE -*/ - - -/* Determine if block pool performance gathering is required by the application. When the following is - defined, ThreadX gathers various block pool performance information. */ - -/* -#define TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if byte pool performance gathering is required by the application. When the following is - defined, ThreadX gathers various byte pool performance information. */ - -/* -#define TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if event flags performance gathering is required by the application. When the following is - defined, ThreadX gathers various event flags performance information. */ - -/* -#define TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if mutex performance gathering is required by the application. When the following is - defined, ThreadX gathers various mutex performance information. */ - -/* -#define TX_MUTEX_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if queue performance gathering is required by the application. When the following is - defined, ThreadX gathers various queue performance information. */ - -/* -#define TX_QUEUE_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if semaphore performance gathering is required by the application. When the following is - defined, ThreadX gathers various semaphore performance information. */ - -/* -#define TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if thread performance gathering is required by the application. When the following is - defined, ThreadX gathers various thread performance information. */ - -/* -#define TX_THREAD_ENABLE_PERFORMANCE_INFO -*/ - -/* Determine if timer performance gathering is required by the application. When the following is - defined, ThreadX gathers various timer performance information. */ - -/* -#define TX_TIMER_ENABLE_PERFORMANCE_INFO -*/ - - -#endif /* TX_USER_H */ diff --git a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/.cproject b/ports/arc_em/metaware/test_sandbox/threadx_sandbox/.cproject deleted file mode 100644 index 84623631..00000000 --- a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/.cproject +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.c b/ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.c deleted file mode 100644 index a750646e..00000000 --- a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.c +++ /dev/null @@ -1,373 +0,0 @@ -/* 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 TX_TEST_STACK_SIZE (1024 * 2) -#define TX_TEST_QUEUE_SIZE (16) -#define TX_TEST_BYTE_POOL_SIZE (1024 * 4) -#define TX_TEST_BLOCK_POOL_SIZE (1024 * 4) - - -/* Define the ThreadX object control blocks... */ - -TX_THREAD tx_test_thread_0; -TX_THREAD tx_test_thread_1; -TX_THREAD tx_test_thread_2; -TX_QUEUE tx_test_queue_0; -TX_SEMAPHORE tx_test_semaphore_0; -TX_MUTEX tx_test_mutex_0; -TX_EVENT_FLAGS_GROUP tx_test_event_flags_0; -TX_BYTE_POOL tx_test_byte_pool_0; -TX_BLOCK_POOL tx_test_block_pool_0; - - -/* Define the counters used in the demo application... */ - -ULONG tx_test_thread_0_counter; -ULONG tx_test_thread_1_counter; -ULONG tx_test_thread_2_counter; - - -/* Define thread prototypes. */ - -void tx_test_thread_0_entry(ULONG thread_input); -void tx_test_thread_1_entry(ULONG thread_input); -void tx_test_thread_2_entry(ULONG thread_input); - - -/* Define the thread stacks. */ - -ULONG tx_test_thread_0_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_1_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_2_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; - - -/* Define other buffers used by the test code. */ - -ULONG tx_test_queue_0_buffer[TX_TEST_QUEUE_SIZE]; -ULONG tx_test_byte_pool_0_buffer[TX_TEST_BYTE_POOL_SIZE / sizeof(ULONG)]; -ULONG tx_test_block_pool_0_buffer[TX_TEST_BLOCK_POOL_SIZE / sizeof(ULONG)]; - - -/* Define test function prototypes. */ - -void tx_test_sleep(); -void tx_test_busy_loop(); - -int tx_test_sum_many_params( - void (*f)(), - int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, - int p9, int p10, int p11, int p12, int p13, int p14, int p15, int p16, - int p17, int p18, int p19, int p20, int p21, int p22, int p23, int p24, - int p25, int p26, int p27, int p28, int p29, int p30, int p31, int p32, - int p33, int p34, int p35, int p36, int p37, int p38, int p39, int p40, - int p41, int p42, int p43, int p44, int p45, int p46, int p47, int p48, - int p49, int p50, int p51, int p52, int p53, int p54, int p55, int p56, - int p57, int p58, int p59, int p60, int p61, int p62, int p63, int p64, - int p65, int p66, int p67, int p68, int p69, int p70, int p71, int p72); - -int tx_test_multiply_many_params( - void (*f)(), - int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, - int p9, int p10, int p11, int p12, int p13, int p14, int p15, int p16, - int p17, int p18, int p19, int p20, int p21, int p22, int p23, int p24, - int p25, int p26, int p27, int p28, int p29, int p30, int p31, int p32, - int p33, int p34, int p35, int p36, int p37, int p38, int p39, int p40, - int p41, int p42, int p43, int p44, int p45, int p46, int p47, int p48, - int p49, int p50, int p51, int p52, int p53, int p54, int p55, int p56, - int p57, int p58, int p59, int p60, int p61, int p62, int p63, int p64, - int p65, int p66, int p67, int p68, int p69, int p70, int p71, int p72); - - -/**/ - -void tx_test_error_handler() -{ - for (;;) - { - /* Stay here forever */ - } -} - -/* Define main entry point. */ - -int main() -{ - - /* Enter the ThreadX kernel. */ - tx_kernel_enter(); - - return(0); -} - - -/* Define what the initial system looks like. */ - -void tx_application_define(void *first_unused_memory) -{ - -UINT status = TX_SUCCESS; - - - /* Create the ThreadX test thread 0. */ - status = tx_thread_create( - &tx_test_thread_0, "ThreadX test thread 0", - tx_test_thread_0_entry, 0, - tx_test_thread_0_stack, - TX_TEST_STACK_SIZE, - 5, 5, - TX_NO_TIME_SLICE, - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 1. */ - status = tx_thread_create( - &tx_test_thread_1, "ThreadX test thread 1", - tx_test_thread_1_entry, 1, - tx_test_thread_1_stack, - TX_TEST_STACK_SIZE, - 7, 7, - 3, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 2. */ - status = tx_thread_create( - &tx_test_thread_2, "ThreadX test thread 2", - tx_test_thread_2_entry, 2, - tx_test_thread_2_stack, - TX_TEST_STACK_SIZE, - 5, 7, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the message queue shared by threads 1 and 2. */ - status = tx_queue_create(&tx_test_queue_0, "ThreadX test queue 0", TX_1_ULONG, tx_test_queue_0_buffer, TX_TEST_QUEUE_SIZE*sizeof(ULONG)); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the semaphore used for testing. */ - status = tx_semaphore_create(&tx_test_semaphore_0, "ThreadX test semaphore 0", 1); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the event flags group used by threads 1 and 5. */ - status = tx_event_flags_create(&tx_test_event_flags_0, "ThreadX test event flags 0"); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the mutex used for testing without priority inheritance. */ - status = tx_mutex_create(&tx_test_mutex_0, "ThreadX test mutex 0", TX_NO_INHERIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create a byte memory pool for testing. */ - status = tx_byte_pool_create(&tx_test_byte_pool_0, "ThreadX test byte pool 0", tx_test_byte_pool_0_buffer, TX_TEST_BYTE_POOL_SIZE); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Quick test of the byte pool during initialization. */ - { - CHAR *pointer = TX_NULL; - - /* Allocate a block and release the block memory. */ - status = tx_byte_allocate(&tx_test_byte_pool_0, (VOID **) &pointer, 123, TX_NO_WAIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Release the block back to the pool. */ - status = tx_block_release(pointer); - if (status != TX_SUCCESS) tx_test_error_handler(); - } - - /* Create a block memory pool to allocate a message buffer from. */ - status = tx_block_pool_create(&tx_test_block_pool_0, "block pool 0", sizeof(ULONG), tx_test_block_pool_0_buffer, TX_TEST_BLOCK_POOL_SIZE); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Quick test of the block pool during initialization. */ - { - CHAR *pointer = TX_NULL; - - /* Allocate a block and release the block memory. */ - status = tx_block_allocate(&tx_test_block_pool_0, (VOID **) &pointer, TX_NO_WAIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Release the block back to the pool. */ - status = tx_block_release(pointer); - if (status != TX_SUCCESS) tx_test_error_handler(); - } -} - - - -/* Define the test threads. */ - -void tx_test_thread_0_entry(ULONG thread_input) -{ - -UINT status; - - - /* This thread simply sits in while-forever-sleep loop. */ - while(1) - { - - /* Increment the thread counter. */ - tx_test_thread_0_counter++; - - /* Sleep for 10 ticks. */ - tx_thread_sleep(10); - - /* Set event flag 0 to wakeup thread 5. */ - status = tx_event_flags_set(&tx_test_event_flags_0, 0x1, TX_OR); - - /* Check status. */ - if (status != TX_SUCCESS) - break; - } -} - - -void tx_test_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. */ - tx_test_thread_1_counter++; - - /* Send message to queue 0. */ - status = tx_queue_send(&tx_test_queue_0, &tx_test_thread_1_counter, TX_WAIT_FOREVER); - - /* Check completion status. */ - if (status != TX_SUCCESS) - break; - } -} - - -void tx_test_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. */ - tx_test_thread_2_counter++; - - /* Retrieve a message from the queue. */ - status = tx_queue_receive(&tx_test_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 != tx_test_thread_1_counter)) - break; - } -} - - -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) - { - - /* Get the semaphore with suspension. */ - status = tx_semaphore_get(&tx_test_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(&tx_test_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) - { - - /* Wait for event flag 0. */ - status = tx_event_flags_get(&tx_test_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) - { - - /* Get the mutex with suspension. */ - status = tx_mutex_get(&tx_test_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(&tx_test_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(&tx_test_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(&tx_test_mutex_0); - - /* Check status. */ - if (status != TX_SUCCESS) - break; - } -} diff --git a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.cmd b/ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.cmd deleted file mode 100644 index 78dc1f6e..00000000 --- a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/sample_threadx_validation.cmd +++ /dev/null @@ -1,55 +0,0 @@ -// -// This is the linker script example (SRV3-style). -// (c) Synopsys, 2013 -// -// - -//number of exceptions and interrupts -NUMBER_OF_EXCEPTIONS = 16;//it is fixed (16) -NUMBER_OF_INTERRUPTS = 5;//depends on HW configuration - -//define Interrupt Vector Table size -IVT_SIZE_ITEMS = (NUMBER_OF_EXCEPTIONS + NUMBER_OF_INTERRUPTS);//the total IVT size (in "items") -IVT_SIZE_BYTES = IVT_SIZE_ITEMS * 4;//in bytes - -//define ICCM and DCCM locations -MEMORY { - ICCM: ORIGIN = 0x00000000, LENGTH = 128K - DCCM: ORIGIN = 0x80000000, LENGTH = 128K -} - -//define sections and groups -SECTIONS { - GROUP: { - .ivt (TEXT) : # Interrupt table - { - ___ivt1 = .; - * (.ivt) - ___ivt2 = .; - // Make the IVT at least IVT_SIZE_BYTES - . += (___ivt2 - ___ivt1 < IVT_SIZE_BYTES) ? (IVT_SIZE_BYTES - (___ivt2 - ___ivt1)) : 0; - } - .ivh (TEXT) : // Interrupt handlers - - //TEXT sections - .text? : { *('.text$crt*') } - * (TEXT): {} - //Literals - * (LIT): {} - } > ICCM - - GROUP: { - //data sections - .sdata?: {} - .sbss?: {} - *(DATA): {} - *(BSS): {} - //stack - .stack_top: {} - .stack ALIGN(4) SIZE(DEFINED _STACKSIZE?_STACKSIZE:4096): {} - .stack_base: {} - //heap (empty) - .heap? ALIGN(4) SIZE(DEFINED _HEAPSIZE?_HEAPSIZE:0): {} - .free_memory: {} - } > DCCM - } diff --git a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/tx_initialize_low_level.s b/ports/arc_em/metaware/test_sandbox/threadx_sandbox/tx_initialize_low_level.s deleted file mode 100644 index bea01020..00000000 --- a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/tx_initialize_low_level.s +++ /dev/null @@ -1,360 +0,0 @@ -;/**************************************************************************/ -;/* */ -;/* Copyright (c) Microsoft Corporation. All rights reserved. */ -;/* */ -;/* This software is licensed under the Microsoft Software License */ -;/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -;/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -;/* and in the root directory of this software. */ -;/* */ -;/**************************************************************************/ - -;/**************************************************************************/ -;/**************************************************************************/ -;/** */ -;/** ThreadX Component */ -;/** */ -;/** Initialize */ -;/** */ -;/**************************************************************************/ -;/**************************************************************************/ - - .equ IRQ_SELECT, 0x40B - .equ KSTACK_TOP, 0x264 - .equ KSTACK_BASE, 0x265 - .equ STATUS32_SC, 0x4000 - -; -; /* Define section for placement after all linker allocated RAM memory. This -; is used to calculate the first free address that is passed to -; tx_appication_define, soley for the ThreadX application's use. */ -; - .section ".free_memory","aw" - .align 4 - .global _tx_first_free_address -_tx_first_free_address: - .space 4 -; -; /* Define section for placement before the main stack area for setting -; up the STACK_TOP address for hardware stack checking. */ -; - .section ".stack_top","aw" - .align 4 - .global _tx_system_stack_top_address -_tx_system_stack_top_address: - .space 4 -; -; /* Define section for placement after the main stack area for setting -; up the STACK_BASE address for hardware stack checking. */ -; - .section ".stack_base","aw" - .align 4 - .global _tx_system_stack_base_address -_tx_system_stack_base_address: - .space 4 -; -; - .text -;/**************************************************************************/ -;/* */ -;/* FUNCTION RELEASE */ -;/* */ -;/* _tx_initialize_low_level ARCv2_EM/MetaWare */ -;/* 6.x */ -;/* AUTHOR */ -;/* */ -;/* William E. Lamie, 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 */ -;/* */ -;/* RELEASE HISTORY */ -;/* */ -;/* DATE NAME DESCRIPTION */ -;/* */ -;/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -;/* */ -;/**************************************************************************/ -;VOID _tx_initialize_low_level(VOID) -;{ - .global _tx_initialize_low_level - .type _tx_initialize_low_level, @function -_tx_initialize_low_level: - - .ifdef TX_ENABLE_HW_STACK_CHECKING - mov r0, _tx_system_stack_top_address ; Pickup top of system stack (lowest memory address) - sr r0, [KSTACK_TOP] ; Setup KSTACK_TOP - mov r0, _tx_system_stack_base_address ; Pickup base of system stack (highest memory address) - sr r0, [KSTACK_BASE] ; Setup KSTACK_BASE - lr r0, [status32] ; Pickup current STATUS32 - or r0, r0, STATUS32_SC ; Or in hardware stack checking enable bit (SC) - kflag r0 ; Enable hardware stack checking - .endif -; -; /* Save the system stack pointer. */ -; _tx_thread_system_stack_ptr = (VOID_PTR) (sp); -; - mov r0, _estack ; Pickup the end of stack address - st r0, [gp, _tx_thread_system_stack_ptr@sda] ; Save system stack pointer -; -; -; /* Pickup the first available memory address. */ -; - mov r0, _tx_first_free_address ; Pickup first free memory address -; -; /* Save the first available memory address. */ -; _tx_initialize_unused_memory = (VOID_PTR) _end; -; - st r0, [gp, _tx_initialize_unused_memory@sda] -; -; -; /* Setup Timer 0 for periodic interrupts at interrupt vector 16. */ -; - mov r0, 0 ; Disable additional ISR reg saving/restoring - sr r0, [AUX_IRQ_CTRL] ; - - mov r0, 16 ; Select timer 0 - sr r0, [IRQ_SELECT] ; - mov r0, 15 ; Set timer 0 to priority 15 - sr r0, [IRQ_PRIORITY] ; - mov r0, 1 ; Enable this interrupt - sr r0, [IRQ_ENABLE] ; - mov r0, 0x10000 ; Setup timer period - sr r0, [LIMIT0] ; - mov r0, 0 ; Clear timer 0 current count - sr r0, [COUNT0] ; - mov r0, 3 ; Enable timer 0 - sr r0, [CONTROL0] ; - - .ifdef TX_TIMER_1_SETUP - mov r0, 17 ; Select timer 1 - sr r0, [IRQ_SELECT] ; - mov r0, 2 ; Set timer 1 to priority 14 - sr r0, [IRQ_PRIORITY] ; - mov r0, 1 ; Enable this interrupt - sr r0, [IRQ_ENABLE] ; - mov r0, 0x10020 ; Setup timer period - sr r0, [LIMIT1] ; - mov r0, 0 ; Clear timer 0 current count - sr r0, [COUNT1] ; - mov r0, 3 ; Enable timer 0 - sr r0, [CONTROL1] ; - .endif -; -; /* Done, return to caller. */ -; - j_s.d [blink] ; Return to caller - nop -;} -; -; -; /* Define default vector table entries. */ -; - .global _tx_memory_error -_tx_memory_error: - flag 1 - nop - nop - nop - b _tx_memory_error - - .global _tx_instruction_error -_tx_instruction_error: - flag 1 - nop - nop - nop - b _tx_instruction_error - - .global _tx_ev_machine_check -_tx_ev_machine_check: - flag 1 - nop - nop - nop - b _tx_ev_machine_check - - .global _tx_ev_tblmiss_inst -_tx_ev_tblmiss_inst: - flag 1 - nop - nop - nop - b _tx_ev_tblmiss_inst - - .global _tx_ev_tblmiss_data -_tx_ev_tblmiss_data: - flag 1 - nop - nop - nop - b _tx_ev_tblmiss_data - - .global _tx_ev_protection_viol -_tx_ev_protection_viol: - flag 1 - nop - nop - nop - b _tx_ev_protection_viol - - .global _tx_ev_privilege_viol -_tx_ev_privilege_viol: - flag 1 - nop - nop - nop - b _tx_ev_privilege_viol - - .global _tx_ev_software_int -_tx_ev_software_int: - flag 1 - nop - nop - nop - b _tx_ev_software_int - - .global _tx_ev_trap -_tx_ev_trap: - flag 1 - nop - nop - nop - b _tx_ev_trap - - .global _tx_ev_extension -_tx_ev_extension: - flag 1 - nop - nop - nop - b _tx_ev_extension - - .global _tx_ev_divide_by_zero -_tx_ev_divide_by_zero: - flag 1 - nop - nop - nop - b _tx_ev_divide_by_zero - - .global _tx_ev_dc_error -_tx_ev_dc_error: - flag 1 - nop - nop - nop - b _tx_ev_dc_error - - .global _tx_ev_maligned -_tx_ev_maligned: - flag 1 - nop - nop - nop - b _tx_ev_maligned - - .global _tx_unsued_0 -_tx_unsued_0: - flag 1 - nop - nop - nop - b _tx_unsued_0 - - .global _tx_unused_1 -_tx_unused_1: - flag 1 - nop - nop - nop - b _tx_unused_1 - - .global _tx_timer_0 -_tx_timer_0: -; -; /* By default, setup Timer 0 as the ThreadX timer interrupt. */ -; - sub sp, sp, 160 ; Allocate an interrupt stack frame - st r0, [sp, 0] ; Save r0 - st r1, [sp, 4] ; Save r1 - st r2, [sp, 8] ; Save r2 - mov r0, 3 - sr r0, [CONTROL0] - - b _tx_timer_interrupt ; Jump to generic ThreadX timer interrupt - ; handler -; flag 1 -; nop -; nop -; nop -; b _tx_timer_0 - - .global _tx_timer_1 -_tx_timer_1: - sub sp, sp, 160 ; Allocate an interrupt stack frame - st blink, [sp, 16] ; Save blink - bl _tx_thread_context_save ; Call context save -; -; /* ISR processing goes here. If the applications wishes to re-enable -; interrupts, the SETI instruction can be used here. Also note that -; register usage in assembly code must be confined to the compiler -; scratch registers. */ -; - mov r0, 3 - sr r0, [CONTROL1] -; - b _tx_thread_context_restore ; Call context restore - -; flag 1 -; nop -; nop -; nop -; b _tx_timer_1 - - .global _tx_undefined_0 -_tx_undefined_0: - flag 1 - nop - nop - nop - b _tx_undefined_0 - - .global _tx_undefined_1 -_tx_undefined_1: - flag 1 - nop - nop - nop - b _tx_undefined_1 - - .global _tx_undefined_2 -_tx_undefined_2: - flag 1 - nop - nop - nop - b _tx_undefined_2 - - .end diff --git a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/vectors.s b/ports/arc_em/metaware/test_sandbox/threadx_sandbox/vectors.s deleted file mode 100644 index c6cbc893..00000000 --- a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/vectors.s +++ /dev/null @@ -1,29 +0,0 @@ - -.file "vectors.s" -.section .ivt,text -;; This directive forces this section to stay resident even if stripped out by the -zpurgetext linker option -.sectflag .ivt,include - -;// handler's name type number name offset in IVT (hex/dec) -.long _start ; exception 0 program entry point offset 0x0 0 -.long _tx_memory_error ; exception 1 memory_error offset 0x4 4 -.long _tx_instruction_error ; exception 2 instruction_error offset 0x8 8 -.long _tx_ev_machine_check ; exception 3 EV_MachineCheck offset 0xC 12 -.long _tx_ev_tblmiss_inst ; exception 4 EV_TLBMissI offset 0x10 16 -.long _tx_ev_tblmiss_data ; exception 5 EV_TLBMissD offset 0x14 20 -.long _tx_ev_protection_viol ; exception 6 EV_ProtV offset 0x18 24 -.long _tx_ev_privilege_viol ; exception 7 EV_PrivilegeV offset 0x1C 28 -.long _tx_ev_software_int ; exception 8 EV_SWI offset 0x20 32 -.long _tx_ev_trap ; exception 9 EV_Trap offset 0x24 36 -.long _tx_ev_extension ; exception 10 EV_Extension offset 0x28 40 -.long _tx_ev_divide_by_zero ; exception 11 EV_DivZero offset 0x2C 44 -.long _tx_ev_dc_error ; exception 12 EV_DCError offset 0x30 48 -.long _tx_ev_maligned ; exception 13 EV_Maligned offset 0x34 52 -.long _tx_unsued_0 ; exception 14 unused offset 0x38 56 -.long _tx_unused_1 ; exception 15 unused offset 0x3C 60 -.long _tx_timer_0 ; IRQ 16 Timer 0 offset 0x40 64 -.long _tx_timer_1 ; IRQ 17 Timer 1 offset 0x44 68 -.long _tx_undefined_0 ; IRQ 18 offset 0x48 72 -.long _tx_undefined_1 ; IRQ 19 offset 0x4C 76 -.long _tx_undefined_2 ; IRQ 20 offset 0x50 80 - diff --git a/ports/arc_em/metaware/test_sandbox/tx/.cproject b/ports/arc_em/metaware/test_sandbox/tx/.cproject deleted file mode 100644 index ce329051..00000000 --- a/ports/arc_em/metaware/test_sandbox/tx/.cproject +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/arc_em/metaware/test_validation/threadx_validation/.cproject b/ports/arc_em/metaware/test_validation/threadx_validation/.cproject deleted file mode 100644 index 84623631..00000000 --- a/ports/arc_em/metaware/test_validation/threadx_validation/.cproject +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.c b/ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.c deleted file mode 100644 index 48451374..00000000 --- a/ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.c +++ /dev/null @@ -1,654 +0,0 @@ -/* 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 TX_TEST_STACK_SIZE (1024 * 2) -#define TX_TEST_QUEUE_SIZE (16) -#define TX_TEST_BYTE_POOL_SIZE (1024 * 4) -#define TX_TEST_BLOCK_POOL_SIZE (1024 * 4) - - -/* Define the ThreadX object control blocks... */ - -TX_THREAD tx_test_thread_0; -TX_THREAD tx_test_thread_1; -TX_THREAD tx_test_thread_2; -TX_THREAD tx_test_thread_3; -TX_THREAD tx_test_thread_4; -TX_THREAD tx_test_thread_5; -TX_THREAD tx_test_thread_6; -TX_THREAD tx_test_thread_7; -TX_QUEUE tx_test_queue_0; -TX_SEMAPHORE tx_test_semaphore_0; -TX_MUTEX tx_test_mutex_0; -TX_EVENT_FLAGS_GROUP tx_test_event_flags_0; -TX_BYTE_POOL tx_test_byte_pool_0; -TX_BLOCK_POOL tx_test_block_pool_0; - - -/* Define the counters used in the demo application... */ - -ULONG tx_test_error_counter; -ULONG tx_test_thread_0_counter; -ULONG tx_test_thread_1_counter; -ULONG tx_test_thread_2_counter; - - -/* Define thread prototypes. */ - -void tx_test_thread_0_entry(ULONG thread_input); -void tx_test_thread_1_entry(ULONG thread_input); -void tx_test_thread_2_entry(ULONG thread_input); -void tx_test_thread_3_and_4_entry(ULONG thread_input); -void tx_test_thread_5_entry(ULONG thread_input); -void tx_test_thread_6_and_7_entry(ULONG thread_input); - - -/* Define the thread stacks. */ - -ULONG tx_test_thread_0_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_1_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_2_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_3_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_4_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_5_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_6_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; -ULONG tx_test_thread_7_stack[TX_TEST_STACK_SIZE / sizeof(ULONG)]; - - -/* Define other buffers used by the test code. */ - -ULONG tx_test_queue_0_buffer[TX_TEST_QUEUE_SIZE]; -ULONG tx_test_byte_pool_0_buffer[TX_TEST_BYTE_POOL_SIZE / sizeof(ULONG)]; -ULONG tx_test_block_pool_0_buffer[TX_TEST_BLOCK_POOL_SIZE / sizeof(ULONG)]; - - -/* Define test function prototypes. */ - -void tx_test_sleep(); -void tx_test_busy_loop(); -void tx_test_byte_alloc_and_free(); - -int tx_test_sum_many_params( - void (*f)(), - int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, - int p9, int p10, int p11, int p12, int p13, int p14, int p15, int p16, - int p17, int p18, int p19, int p20, int p21, int p22, int p23, int p24, - int p25, int p26, int p27, int p28, int p29, int p30, int p31, int p32, - int p33, int p34, int p35, int p36, int p37, int p38, int p39, int p40, - int p41, int p42, int p43, int p44, int p45, int p46, int p47, int p48, - int p49, int p50, int p51, int p52, int p53, int p54, int p55, int p56, - int p57, int p58, int p59, int p60, int p61, int p62, int p63, int p64, - int p65, int p66, int p67, int p68, int p69, int p70, int p71, int p72); - -int tx_test_xor_many_params( - void (*f)(), - int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, - int p9, int p10, int p11, int p12, int p13, int p14, int p15, int p16, - int p17, int p18, int p19, int p20, int p21, int p22, int p23, int p24, - int p25, int p26, int p27, int p28, int p29, int p30, int p31, int p32, - int p33, int p34, int p35, int p36, int p37, int p38, int p39, int p40, - int p41, int p42, int p43, int p44, int p45, int p46, int p47, int p48, - int p49, int p50, int p51, int p52, int p53, int p54, int p55, int p56, - int p57, int p58, int p59, int p60, int p61, int p62, int p63, int p64, - int p65, int p66, int p67, int p68, int p69, int p70, int p71, int p72); - - -/* Define the error handler. */ - -void tx_test_error_handler() -{ - tx_test_error_counter++; - - for (;;) - { - /* Stay here forever */ - } -} - -/* Define main entry point. */ - -int main() -{ - - /* Enter the ThreadX kernel. */ - tx_kernel_enter(); - - return(0); -} - - -/* Define what the initial system looks like. */ - -void tx_application_define(void *first_unused_memory) -{ - -UINT status = TX_SUCCESS; - - - /* Create the ThreadX test thread 0. */ - status = tx_thread_create( - &tx_test_thread_0, "ThreadX test thread 0", - tx_test_thread_0_entry, 0, - tx_test_thread_0_stack, - TX_TEST_STACK_SIZE, - 5, 5, - TX_NO_TIME_SLICE, - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 1. */ - status = tx_thread_create( - &tx_test_thread_1, "ThreadX test thread 1", - tx_test_thread_1_entry, 1, - tx_test_thread_1_stack, - TX_TEST_STACK_SIZE, - 7, 7, - 3, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 2. */ - status = tx_thread_create( - &tx_test_thread_2, "ThreadX test thread 2", - tx_test_thread_2_entry, 2, - tx_test_thread_2_stack, - TX_TEST_STACK_SIZE, - 7, 5, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 3. */ - status = tx_thread_create( - &tx_test_thread_3, "ThreadX test thread 3", - tx_test_thread_3_and_4_entry, 3, - tx_test_thread_3_stack, - TX_TEST_STACK_SIZE, - 7, 5, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 4. */ - status = tx_thread_create( - &tx_test_thread_4, "ThreadX test thread 4", - tx_test_thread_3_and_4_entry, 4, - tx_test_thread_4_stack, - TX_TEST_STACK_SIZE, - 7, 7, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 5. */ - status = tx_thread_create( - &tx_test_thread_5, "ThreadX test thread 5", - tx_test_thread_5_entry, 5, - tx_test_thread_5_stack, - TX_TEST_STACK_SIZE, - 7, 7, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 6. */ - status = tx_thread_create( - &tx_test_thread_6, "ThreadX test thread 6", - tx_test_thread_6_and_7_entry, 6, - tx_test_thread_6_stack, - TX_TEST_STACK_SIZE, - 7, 7, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the ThreadX test thread 2. */ - status = tx_thread_create( - &tx_test_thread_7, "ThreadX test thread 7", - tx_test_thread_6_and_7_entry, 7, - tx_test_thread_7_stack, - TX_TEST_STACK_SIZE, - 7, 7, - 4, /* time slice */ - TX_AUTO_START); - if (status != TX_SUCCESS) tx_test_error_handler(); - - - /* Create the message queue shared by threads 1 and 2. */ - status = tx_queue_create(&tx_test_queue_0, "ThreadX test queue 0", TX_1_ULONG, tx_test_queue_0_buffer, TX_TEST_QUEUE_SIZE*sizeof(ULONG)); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the semaphore used for testing. */ - status = tx_semaphore_create(&tx_test_semaphore_0, "ThreadX test semaphore 0", 1); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the event flags group used by threads 1 and 5. */ - status = tx_event_flags_create(&tx_test_event_flags_0, "ThreadX test event flags 0"); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create the mutex used for testing without priority inheritance. */ - status = tx_mutex_create(&tx_test_mutex_0, "ThreadX test mutex 0", TX_NO_INHERIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Create a byte memory pool for testing. */ - status = tx_byte_pool_create(&tx_test_byte_pool_0, "ThreadX test byte pool 0", tx_test_byte_pool_0_buffer, TX_TEST_BYTE_POOL_SIZE); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Quick test of the byte pool during initialization. */ - { - CHAR *pointer = TX_NULL; - - /* Allocate a block and release the block memory. */ - status = tx_byte_allocate(&tx_test_byte_pool_0, (VOID **) &pointer, 123, TX_NO_WAIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Release the block back to the pool. */ - status = tx_byte_release(pointer); - if (status != TX_SUCCESS) tx_test_error_handler(); - } - - /* Create a block memory pool to allocate a message buffer from. */ - status = tx_block_pool_create(&tx_test_block_pool_0, "ThreadX test block pool 0", 128, tx_test_block_pool_0_buffer, TX_TEST_BLOCK_POOL_SIZE); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Quick test of the block pool during initialization. */ - { - CHAR *pointer = TX_NULL; - - /* Allocate a block and release the block memory. */ - status = tx_block_allocate(&tx_test_block_pool_0, (VOID **) &pointer, TX_NO_WAIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Release the block back to the pool. */ - status = tx_block_release(pointer); - if (status != TX_SUCCESS) tx_test_error_handler(); - } -} - - -/* Define the test functions. */ - -void tx_test_sleep() -{ - tx_thread_sleep(10); -} - -void tx_test_busy_loop() -{ - unsigned i; - static volatile unsigned x = 0; - for (i = 0; i < (1024 * 4); ++i) - { - x++; - } -} - -void tx_test_byte_alloc_and_free() -{ - UINT status; - - /* Quick test of the byte pool during initialization. */ - { - CHAR *pointer = TX_NULL; - - /* Allocate a block and release the block memory. */ - status = tx_byte_allocate(&tx_test_byte_pool_0, (VOID **) &pointer, 123, TX_NO_WAIT); - if (status != TX_SUCCESS) tx_test_error_handler(); - - /* Release the block back to the pool. */ - status = tx_byte_release(pointer); - if (status != TX_SUCCESS) tx_test_error_handler(); - } - -} - -int tx_test_sum_many_params( - void (*f)(), - int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, - int p9, int p10, int p11, int p12, int p13, int p14, int p15, int p16, - int p17, int p18, int p19, int p20, int p21, int p22, int p23, int p24, - int p25, int p26, int p27, int p28, int p29, int p30, int p31, int p32, - int p33, int p34, int p35, int p36, int p37, int p38, int p39, int p40, - int p41, int p42, int p43, int p44, int p45, int p46, int p47, int p48, - int p49, int p50, int p51, int p52, int p53, int p54, int p55, int p56, - int p57, int p58, int p59, int p60, int p61, int p62, int p63, int p64, - int p65, int p66, int p67, int p68, int p69, int p70, int p71, int p72) -{ - volatile int a,b; - - a = - p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + - p9 + p10 + p11 + p12 + p13 + p14 + p15 + p16 + - p17 + p18 + p19 + p20 + p21 + p22 + p23 + p24 + - p25 + p26 + p27 + p28 + p29 + p30 + p31 + p32 + - p33 + p34 + p35 + p36 + p37 + p38 + p39 + p40 + - p41 + p42 + p43 + p44 + p45 + p46 + p47 + p48 + - p49 + p50 + p51 + p52 + p53 + p54 + p55 + p56 + - p57 + p58 + p59 + p60 + p61 + p62 + p63 + p64 + - p65 + p66 + p67 + p68 + p69 + p70 + p71 + p72; - - f(); - - b = - p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + - p9 + p10 + p11 + p12 + p13 + p14 + p15 + p16 + - p17 + p18 + p19 + p20 + p21 + p22 + p23 + p24 + - p25 + p26 + p27 + p28 + p29 + p30 + p31 + p32 + - p33 + p34 + p35 + p36 + p37 + p38 + p39 + p40 + - p41 + p42 + p43 + p44 + p45 + p46 + p47 + p48 + - p49 + p50 + p51 + p52 + p53 + p54 + p55 + p56 + - p57 + p58 + p59 + p60 + p61 + p62 + p63 + p64 + - p65 + p66 + p67 + p68 + p69 + p70 + p71 + p72; - - if (a != b) - { - tx_test_error_handler(); - } - - return a; -} - -int tx_test_xor_many_params( - void (*f)(), - int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8, - int p9, int p10, int p11, int p12, int p13, int p14, int p15, int p16, - int p17, int p18, int p19, int p20, int p21, int p22, int p23, int p24, - int p25, int p26, int p27, int p28, int p29, int p30, int p31, int p32, - int p33, int p34, int p35, int p36, int p37, int p38, int p39, int p40, - int p41, int p42, int p43, int p44, int p45, int p46, int p47, int p48, - int p49, int p50, int p51, int p52, int p53, int p54, int p55, int p56, - int p57, int p58, int p59, int p60, int p61, int p62, int p63, int p64, - int p65, int p66, int p67, int p68, int p69, int p70, int p71, int p72) -{ - volatile int a,b; - - a = - p1 ^ p2 ^ p3 ^ p4 ^ p5 ^ p6 ^ p7 ^ p8 ^ - p9 ^ p10 ^ p11 ^ p12 ^ p13 ^ p14 ^ p15 ^ p16 ^ - p17 ^ p18 ^ p19 ^ p20 ^ p21 ^ p22 ^ p23 ^ p24 ^ - p25 ^ p26 ^ p27 ^ p28 ^ p29 ^ p30 ^ p31 ^ p32 ^ - p33 ^ p34 ^ p35 ^ p36 ^ p37 ^ p38 ^ p39 ^ p40 ^ - p41 ^ p42 ^ p43 ^ p44 ^ p45 ^ p46 ^ p47 ^ p48 ^ - p49 ^ p50 ^ p51 ^ p52 ^ p53 ^ p54 ^ p55 ^ p56 ^ - p57 ^ p58 ^ p59 ^ p60 ^ p61 ^ p62 ^ p63 ^ p64 ^ - p65 ^ p66 ^ p67 ^ p68 ^ p69 ^ p70 ^ p71 ^ p72; - - f(); - - b = - p1 ^ p2 ^ p3 ^ p4 ^ p5 ^ p6 ^ p7 ^ p8 ^ - p9 ^ p10 ^ p11 ^ p12 ^ p13 ^ p14 ^ p15 ^ p16 ^ - p17 ^ p18 ^ p19 ^ p20 ^ p21 ^ p22 ^ p23 ^ p24 ^ - p25 ^ p26 ^ p27 ^ p28 ^ p29 ^ p30 ^ p31 ^ p32 ^ - p33 ^ p34 ^ p35 ^ p36 ^ p37 ^ p38 ^ p39 ^ p40 ^ - p41 ^ p42 ^ p43 ^ p44 ^ p45 ^ p46 ^ p47 ^ p48 ^ - p49 ^ p50 ^ p51 ^ p52 ^ p53 ^ p54 ^ p55 ^ p56 ^ - p57 ^ p58 ^ p59 ^ p60 ^ p61 ^ p62 ^ p63 ^ p64 ^ - p65 ^ p66 ^ p67 ^ p68 ^ p69 ^ p70 ^ p71 ^ p72; - - if (a != b) - { - tx_test_error_handler(); - } - - return a; -} - - - -/* Define the test threads. */ - -void tx_test_thread_0_entry(ULONG thread_input) -{ - -UINT status; - - - /* This thread simply sits in while-forever-sleep loop. */ - while(1) - { - volatile int a, b, c, d; - - int p1; int p2; int p3; int p4; int p5; int p6; int p7; int p8; - int p9; int p10; int p11; int p12; int p13; int p14; int p15; int p16; - int p17; int p18; int p19; int p20; int p21; int p22; int p23; int p24; - int p25; int p26; int p27; int p28; int p29; int p30; int p31; int p32; - int p33; int p34; int p35; int p36; int p37; int p38; int p39; int p40; - int p41; int p42; int p43; int p44; int p45; int p46; int p47; int p48; - int p49; int p50; int p51; int p52; int p53; int p54; int p55; int p56; - int p57; int p58; int p59; int p60; int p61; int p62; int p63; int p64; - int p65; int p66; int p67; int p68; int p69; int p70; int p71; int p72; - - p1 = 1; p2 = 2; p3 = 3; p4 = 4; p5 = 5; p6 = 6; p7 = 7; p8 = 8; - p9 = 9; p10 = 10; p11 = 11; p12 = 12; p13 = 13; p14 = 14; p15 = 15; p16 = 16; - p17 = 17; p18 = 18; p19 = 19; p20 = 20; p21 = 21; p22 = 22; p23 = 23; p24 = 24; - p25 = 25; p26 = 26; p27 = 27; p28 = 28; p29 = 29; p30 = 30; p31 = 31; p32 = 32; - p33 = 33; p34 = 34; p35 = 35; p36 = 36; p37 = 37; p38 = 38; p39 = 39; p40 = 40; - p41 = 41; p42 = 42; p43 = 43; p44 = 44; p45 = 45; p46 = 46; p47 = 47; p48 = 48; - p49 = 49; p50 = 50; p51 = 51; p52 = 52; p53 = 53; p54 = 54; p55 = 55; p56 = 56; - p57 = 57; p58 = 58; p59 = 59; p60 = 60; p61 = 61; p62 = 62; p63 = 63; p64 = 64; - p65 = 65; p66 = 66; p67 = 67; p68 = 68; p69 = 69; p70 = 70; p71 = 71; p72 = 72; - - a = tx_test_sum_many_params( - tx_test_sleep, - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72); - - b = tx_test_xor_many_params( - tx_test_sleep, - 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72); - - /* Increment the thread counter. */ - tx_test_thread_0_counter++; - - /* Sleep for 10 ticks. */ - tx_thread_sleep(10); - - c = tx_test_sum_many_params( - tx_test_sleep, - p1, p2, p3, p4, p5, p6, p7, p8, - p9, p10, p11, p12, p13, p14, p15, p16, - p17, p18, p19, p20, p21, p22, p23, p24, - p25, p26, p27, p28, p29, p30, p31, p32, - p33, p34, p35, p36, p37, p38, p39, p40, - p41, p42, p43, p44, p45, p46, p47, p48, - p49, p50, p51, p52, p53, p54, p55, p56, - p57, p58, p59, p60, p61, p62, p63, p64, - p65, p66, p67, p68, p69, p70, p71, p72); - - d = tx_test_xor_many_params( - tx_test_sleep, - p1, p2, p3, p4, p5, p6, p7, p8, - p9, p10, p11, p12, p13, p14, p15, p16, - p17, p18, p19, p20, p21, p22, p23, p24, - p25, p26, p27, p28, p29, p30, p31, p32, - p33, p34, p35, p36, p37, p38, p39, p40, - p41, p42, p43, p44, p45, p46, p47, p48, - p49, p50, p51, p52, p53, p54, p55, p56, - p57, p58, p59, p60, p61, p62, p63, p64, - p65, p66, p67, p68, p69, p70, p71, p72); - - if (a != c) - { - tx_test_error_handler(); - } - - if (b != d) - { - tx_test_error_handler(); - } - - /* Set event flag 0 to wakeup thread 5. */ - status = tx_event_flags_set(&tx_test_event_flags_0, 0x1, TX_OR); - - /* Check status. */ - if (status != TX_SUCCESS) - break; - } -} - - -void tx_test_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. */ - tx_test_thread_1_counter++; - - /* Send message to queue 0. */ - status = tx_queue_send(&tx_test_queue_0, &tx_test_thread_1_counter, TX_WAIT_FOREVER); - - /* Check completion status. */ - if (status != TX_SUCCESS) - break; - } -} - - -void tx_test_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. */ - tx_test_thread_2_counter++; - - /* Retrieve a message from the queue. */ - status = tx_queue_receive(&tx_test_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 != tx_test_thread_1_counter)) - break; - } -} - - -void tx_test_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) - { - - /* Get the semaphore with suspension. */ - status = tx_semaphore_get(&tx_test_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(&tx_test_semaphore_0); - - /* Check status. */ - if (status != TX_SUCCESS) - break; - } -} - - -void tx_test_thread_5_entry(ULONG thread_input) -{ - -UINT status; -ULONG actual_flags; - - - /* This thread simply waits for an event in a forever loop. */ - while(1) - { - - /* Wait for event flag 0. */ - status = tx_event_flags_get(&tx_test_event_flags_0, 0x1, TX_OR_CLEAR, - &actual_flags, TX_WAIT_FOREVER); - - /* Check status. */ - if ((status != TX_SUCCESS) || (actual_flags != 0x1)) - break; - } -} - - -void tx_test_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) - { - - /* Get the mutex with suspension. */ - status = tx_mutex_get(&tx_test_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(&tx_test_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(&tx_test_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(&tx_test_mutex_0); - - /* Check status. */ - if (status != TX_SUCCESS) - break; - } -} diff --git a/ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.cmd b/ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.cmd deleted file mode 100644 index 78dc1f6e..00000000 --- a/ports/arc_em/metaware/test_validation/threadx_validation/sample_threadx_validation.cmd +++ /dev/null @@ -1,55 +0,0 @@ -// -// This is the linker script example (SRV3-style). -// (c) Synopsys, 2013 -// -// - -//number of exceptions and interrupts -NUMBER_OF_EXCEPTIONS = 16;//it is fixed (16) -NUMBER_OF_INTERRUPTS = 5;//depends on HW configuration - -//define Interrupt Vector Table size -IVT_SIZE_ITEMS = (NUMBER_OF_EXCEPTIONS + NUMBER_OF_INTERRUPTS);//the total IVT size (in "items") -IVT_SIZE_BYTES = IVT_SIZE_ITEMS * 4;//in bytes - -//define ICCM and DCCM locations -MEMORY { - ICCM: ORIGIN = 0x00000000, LENGTH = 128K - DCCM: ORIGIN = 0x80000000, LENGTH = 128K -} - -//define sections and groups -SECTIONS { - GROUP: { - .ivt (TEXT) : # Interrupt table - { - ___ivt1 = .; - * (.ivt) - ___ivt2 = .; - // Make the IVT at least IVT_SIZE_BYTES - . += (___ivt2 - ___ivt1 < IVT_SIZE_BYTES) ? (IVT_SIZE_BYTES - (___ivt2 - ___ivt1)) : 0; - } - .ivh (TEXT) : // Interrupt handlers - - //TEXT sections - .text? : { *('.text$crt*') } - * (TEXT): {} - //Literals - * (LIT): {} - } > ICCM - - GROUP: { - //data sections - .sdata?: {} - .sbss?: {} - *(DATA): {} - *(BSS): {} - //stack - .stack_top: {} - .stack ALIGN(4) SIZE(DEFINED _STACKSIZE?_STACKSIZE:4096): {} - .stack_base: {} - //heap (empty) - .heap? ALIGN(4) SIZE(DEFINED _HEAPSIZE?_HEAPSIZE:0): {} - .free_memory: {} - } > DCCM - } diff --git a/ports/arc_em/metaware/test_validation/threadx_validation/tx_initialize_low_level.s b/ports/arc_em/metaware/test_validation/threadx_validation/tx_initialize_low_level.s deleted file mode 100644 index bea01020..00000000 --- a/ports/arc_em/metaware/test_validation/threadx_validation/tx_initialize_low_level.s +++ /dev/null @@ -1,360 +0,0 @@ -;/**************************************************************************/ -;/* */ -;/* Copyright (c) Microsoft Corporation. All rights reserved. */ -;/* */ -;/* This software is licensed under the Microsoft Software License */ -;/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -;/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -;/* and in the root directory of this software. */ -;/* */ -;/**************************************************************************/ - -;/**************************************************************************/ -;/**************************************************************************/ -;/** */ -;/** ThreadX Component */ -;/** */ -;/** Initialize */ -;/** */ -;/**************************************************************************/ -;/**************************************************************************/ - - .equ IRQ_SELECT, 0x40B - .equ KSTACK_TOP, 0x264 - .equ KSTACK_BASE, 0x265 - .equ STATUS32_SC, 0x4000 - -; -; /* Define section for placement after all linker allocated RAM memory. This -; is used to calculate the first free address that is passed to -; tx_appication_define, soley for the ThreadX application's use. */ -; - .section ".free_memory","aw" - .align 4 - .global _tx_first_free_address -_tx_first_free_address: - .space 4 -; -; /* Define section for placement before the main stack area for setting -; up the STACK_TOP address for hardware stack checking. */ -; - .section ".stack_top","aw" - .align 4 - .global _tx_system_stack_top_address -_tx_system_stack_top_address: - .space 4 -; -; /* Define section for placement after the main stack area for setting -; up the STACK_BASE address for hardware stack checking. */ -; - .section ".stack_base","aw" - .align 4 - .global _tx_system_stack_base_address -_tx_system_stack_base_address: - .space 4 -; -; - .text -;/**************************************************************************/ -;/* */ -;/* FUNCTION RELEASE */ -;/* */ -;/* _tx_initialize_low_level ARCv2_EM/MetaWare */ -;/* 6.x */ -;/* AUTHOR */ -;/* */ -;/* William E. Lamie, 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 */ -;/* */ -;/* RELEASE HISTORY */ -;/* */ -;/* DATE NAME DESCRIPTION */ -;/* */ -;/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -;/* */ -;/**************************************************************************/ -;VOID _tx_initialize_low_level(VOID) -;{ - .global _tx_initialize_low_level - .type _tx_initialize_low_level, @function -_tx_initialize_low_level: - - .ifdef TX_ENABLE_HW_STACK_CHECKING - mov r0, _tx_system_stack_top_address ; Pickup top of system stack (lowest memory address) - sr r0, [KSTACK_TOP] ; Setup KSTACK_TOP - mov r0, _tx_system_stack_base_address ; Pickup base of system stack (highest memory address) - sr r0, [KSTACK_BASE] ; Setup KSTACK_BASE - lr r0, [status32] ; Pickup current STATUS32 - or r0, r0, STATUS32_SC ; Or in hardware stack checking enable bit (SC) - kflag r0 ; Enable hardware stack checking - .endif -; -; /* Save the system stack pointer. */ -; _tx_thread_system_stack_ptr = (VOID_PTR) (sp); -; - mov r0, _estack ; Pickup the end of stack address - st r0, [gp, _tx_thread_system_stack_ptr@sda] ; Save system stack pointer -; -; -; /* Pickup the first available memory address. */ -; - mov r0, _tx_first_free_address ; Pickup first free memory address -; -; /* Save the first available memory address. */ -; _tx_initialize_unused_memory = (VOID_PTR) _end; -; - st r0, [gp, _tx_initialize_unused_memory@sda] -; -; -; /* Setup Timer 0 for periodic interrupts at interrupt vector 16. */ -; - mov r0, 0 ; Disable additional ISR reg saving/restoring - sr r0, [AUX_IRQ_CTRL] ; - - mov r0, 16 ; Select timer 0 - sr r0, [IRQ_SELECT] ; - mov r0, 15 ; Set timer 0 to priority 15 - sr r0, [IRQ_PRIORITY] ; - mov r0, 1 ; Enable this interrupt - sr r0, [IRQ_ENABLE] ; - mov r0, 0x10000 ; Setup timer period - sr r0, [LIMIT0] ; - mov r0, 0 ; Clear timer 0 current count - sr r0, [COUNT0] ; - mov r0, 3 ; Enable timer 0 - sr r0, [CONTROL0] ; - - .ifdef TX_TIMER_1_SETUP - mov r0, 17 ; Select timer 1 - sr r0, [IRQ_SELECT] ; - mov r0, 2 ; Set timer 1 to priority 14 - sr r0, [IRQ_PRIORITY] ; - mov r0, 1 ; Enable this interrupt - sr r0, [IRQ_ENABLE] ; - mov r0, 0x10020 ; Setup timer period - sr r0, [LIMIT1] ; - mov r0, 0 ; Clear timer 0 current count - sr r0, [COUNT1] ; - mov r0, 3 ; Enable timer 0 - sr r0, [CONTROL1] ; - .endif -; -; /* Done, return to caller. */ -; - j_s.d [blink] ; Return to caller - nop -;} -; -; -; /* Define default vector table entries. */ -; - .global _tx_memory_error -_tx_memory_error: - flag 1 - nop - nop - nop - b _tx_memory_error - - .global _tx_instruction_error -_tx_instruction_error: - flag 1 - nop - nop - nop - b _tx_instruction_error - - .global _tx_ev_machine_check -_tx_ev_machine_check: - flag 1 - nop - nop - nop - b _tx_ev_machine_check - - .global _tx_ev_tblmiss_inst -_tx_ev_tblmiss_inst: - flag 1 - nop - nop - nop - b _tx_ev_tblmiss_inst - - .global _tx_ev_tblmiss_data -_tx_ev_tblmiss_data: - flag 1 - nop - nop - nop - b _tx_ev_tblmiss_data - - .global _tx_ev_protection_viol -_tx_ev_protection_viol: - flag 1 - nop - nop - nop - b _tx_ev_protection_viol - - .global _tx_ev_privilege_viol -_tx_ev_privilege_viol: - flag 1 - nop - nop - nop - b _tx_ev_privilege_viol - - .global _tx_ev_software_int -_tx_ev_software_int: - flag 1 - nop - nop - nop - b _tx_ev_software_int - - .global _tx_ev_trap -_tx_ev_trap: - flag 1 - nop - nop - nop - b _tx_ev_trap - - .global _tx_ev_extension -_tx_ev_extension: - flag 1 - nop - nop - nop - b _tx_ev_extension - - .global _tx_ev_divide_by_zero -_tx_ev_divide_by_zero: - flag 1 - nop - nop - nop - b _tx_ev_divide_by_zero - - .global _tx_ev_dc_error -_tx_ev_dc_error: - flag 1 - nop - nop - nop - b _tx_ev_dc_error - - .global _tx_ev_maligned -_tx_ev_maligned: - flag 1 - nop - nop - nop - b _tx_ev_maligned - - .global _tx_unsued_0 -_tx_unsued_0: - flag 1 - nop - nop - nop - b _tx_unsued_0 - - .global _tx_unused_1 -_tx_unused_1: - flag 1 - nop - nop - nop - b _tx_unused_1 - - .global _tx_timer_0 -_tx_timer_0: -; -; /* By default, setup Timer 0 as the ThreadX timer interrupt. */ -; - sub sp, sp, 160 ; Allocate an interrupt stack frame - st r0, [sp, 0] ; Save r0 - st r1, [sp, 4] ; Save r1 - st r2, [sp, 8] ; Save r2 - mov r0, 3 - sr r0, [CONTROL0] - - b _tx_timer_interrupt ; Jump to generic ThreadX timer interrupt - ; handler -; flag 1 -; nop -; nop -; nop -; b _tx_timer_0 - - .global _tx_timer_1 -_tx_timer_1: - sub sp, sp, 160 ; Allocate an interrupt stack frame - st blink, [sp, 16] ; Save blink - bl _tx_thread_context_save ; Call context save -; -; /* ISR processing goes here. If the applications wishes to re-enable -; interrupts, the SETI instruction can be used here. Also note that -; register usage in assembly code must be confined to the compiler -; scratch registers. */ -; - mov r0, 3 - sr r0, [CONTROL1] -; - b _tx_thread_context_restore ; Call context restore - -; flag 1 -; nop -; nop -; nop -; b _tx_timer_1 - - .global _tx_undefined_0 -_tx_undefined_0: - flag 1 - nop - nop - nop - b _tx_undefined_0 - - .global _tx_undefined_1 -_tx_undefined_1: - flag 1 - nop - nop - nop - b _tx_undefined_1 - - .global _tx_undefined_2 -_tx_undefined_2: - flag 1 - nop - nop - nop - b _tx_undefined_2 - - .end diff --git a/ports/arc_em/metaware/test_validation/threadx_validation/vectors.s b/ports/arc_em/metaware/test_validation/threadx_validation/vectors.s deleted file mode 100644 index c6cbc893..00000000 --- a/ports/arc_em/metaware/test_validation/threadx_validation/vectors.s +++ /dev/null @@ -1,29 +0,0 @@ - -.file "vectors.s" -.section .ivt,text -;; This directive forces this section to stay resident even if stripped out by the -zpurgetext linker option -.sectflag .ivt,include - -;// handler's name type number name offset in IVT (hex/dec) -.long _start ; exception 0 program entry point offset 0x0 0 -.long _tx_memory_error ; exception 1 memory_error offset 0x4 4 -.long _tx_instruction_error ; exception 2 instruction_error offset 0x8 8 -.long _tx_ev_machine_check ; exception 3 EV_MachineCheck offset 0xC 12 -.long _tx_ev_tblmiss_inst ; exception 4 EV_TLBMissI offset 0x10 16 -.long _tx_ev_tblmiss_data ; exception 5 EV_TLBMissD offset 0x14 20 -.long _tx_ev_protection_viol ; exception 6 EV_ProtV offset 0x18 24 -.long _tx_ev_privilege_viol ; exception 7 EV_PrivilegeV offset 0x1C 28 -.long _tx_ev_software_int ; exception 8 EV_SWI offset 0x20 32 -.long _tx_ev_trap ; exception 9 EV_Trap offset 0x24 36 -.long _tx_ev_extension ; exception 10 EV_Extension offset 0x28 40 -.long _tx_ev_divide_by_zero ; exception 11 EV_DivZero offset 0x2C 44 -.long _tx_ev_dc_error ; exception 12 EV_DCError offset 0x30 48 -.long _tx_ev_maligned ; exception 13 EV_Maligned offset 0x34 52 -.long _tx_unsued_0 ; exception 14 unused offset 0x38 56 -.long _tx_unused_1 ; exception 15 unused offset 0x3C 60 -.long _tx_timer_0 ; IRQ 16 Timer 0 offset 0x40 64 -.long _tx_timer_1 ; IRQ 17 Timer 1 offset 0x44 68 -.long _tx_undefined_0 ; IRQ 18 offset 0x48 72 -.long _tx_undefined_1 ; IRQ 19 offset 0x4C 76 -.long _tx_undefined_2 ; IRQ 20 offset 0x50 80 - diff --git a/ports/arc_em/metaware/test_validation/tx/.cproject b/ports/arc_em/metaware/test_validation/tx/.cproject deleted file mode 100644 index ce329051..00000000 --- a/ports/arc_em/metaware/test_validation/tx/.cproject +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports/arc_hs/metaware/example_build/sample_threadx/sample_threadx.c b/ports/arc_hs/metaware/example_build/sample_threadx/sample_threadx.c index 5a03f35c..81cca72b 100644 --- a/ports/arc_hs/metaware/example_build/sample_threadx/sample_threadx.c +++ b/ports/arc_hs/metaware/example_build/sample_threadx/sample_threadx.c @@ -82,7 +82,7 @@ CHAR *pointer = TX_NULL; 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, + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, pointer, DEMO_STACK_SIZE, 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); diff --git a/ports/arc_hs/metaware/example_build/sample_threadx/tx_initialize_low_level.s b/ports/arc_hs/metaware/example_build/sample_threadx/tx_initialize_low_level.s index af5b147c..2ea4513f 100644 --- a/ports/arc_hs/metaware/example_build/sample_threadx/tx_initialize_low_level.s +++ b/ports/arc_hs/metaware/example_build/sample_threadx/tx_initialize_low_level.s @@ -39,7 +39,7 @@ _tx_first_free_address: ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_initialize_low_level ARC_HS/MetaWare */ -;/* 6.1.9 */ +;/* 6.1.10 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -76,6 +76,11 @@ _tx_first_free_address: ;/* 10-15-2021 Andres Mlinar Modified comment(s), optimized*/ ;/* system stack usage, */ ;/* resulting in version 6.1.9 */ +;/* 01-31-2022 Andres Mlinar Modified comments(s), */ +;/* initialize interrupts right */ +;/* before enabling the task */ +;/* scheduler, */ +;/* resulting in version 6.1.10 */ ;/* */ ;/**************************************************************************/ ;VOID _tx_initialize_low_level(VOID) @@ -101,6 +106,18 @@ _tx_initialize_low_level: ; st r0, [gp, _tx_initialize_unused_memory@sda] ; +; /* Done, return to caller. */ +; + j_s.d [blink] ; Return to caller + nop +;} +; +;VOID _tx_initialize_start_interrupts(VOID) +;{ + .align 4 + .global _tx_initialize_start_interrupts + .type _tx_initialize_start_interrupts, @function +_tx_initialize_start_interrupts: ; ; /* Setup Timer 0 for periodic interrupts at interrupt vector 16. */ ; diff --git a/ports/arc_hs/metaware/inc/tx_port.h b/ports/arc_hs/metaware/inc/tx_port.h index dd2b4e42..5d61c45c 100644 --- a/ports/arc_hs/metaware/inc/tx_port.h +++ b/ports/arc_hs/metaware/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h ARC_HS/MetaWare */ -/* 6.1.6 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -48,9 +48,14 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 04-02-2021 Bhupendra Naphade Modified comment(s), updated */ /* macro definition, */ /* resulting in version 6.1.6 */ +/* 01-31-2022 Andres Mlinar Modified comments(s), */ +/* initialize interrupts right */ +/* before enabling the task */ +/* scheduler, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -184,6 +189,12 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_INLINE_INITIALIZATION #endif +/* Define the ARC-specific initialization code that is expanded in the generic source. */ + +void _tx_initialize_start_interrupts(void); + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION _tx_initialize_start_interrupts(); + /* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack @@ -325,7 +336,7 @@ VOID tx_thread_register_bank_assign(VOID *thread_ptr, UINT register_bank); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARC_HS/MetaWare Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARC_HS/MetaWare Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports/arc_hs/metaware/src/tx_thread_interrupt_control.s b/ports/arc_hs/metaware/src/tx_thread_interrupt_control.s index 0a85e979..6e1adb89 100644 --- a/ports/arc_hs/metaware/src/tx_thread_interrupt_control.s +++ b/ports/arc_hs/metaware/src/tx_thread_interrupt_control.s @@ -72,7 +72,7 @@ _tx_thread_interrupt_control: ; /* Apply the new interrupt posture. */ ; seti r0 ; Set desired interrupt state - j_s.d [blink] ; Return to caller with delay slot + j_s.d [blink] ; Return to caller with delay slot mov r0, r1 ; Return previous mask value. Return value is TX_INT_DISABLE or TX_INT_ENABLE. ; ;} diff --git a/ports/arm11/gnu/example_build/libc.a b/ports/arm11/gnu/example_build/libc.a deleted file mode 100644 index b03b1626469d3977b9515263f1a5da2221d7d407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1207876 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½S9!kk(}Y7v*I6VTXu|mJ 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 BnTh}a diff --git a/ports/arm11/gnu/example_build/libgcc.a b/ports/arm11/gnu/example_build/libgcc.a deleted file mode 100644 index da2319e054c6692bb3de60fa55da6df1a4688f6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 261004 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 z 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 z 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 nKB9ljYz4g)KRy?58ys@XzAcgu1GaBo}Eml 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 z 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

Q>dP6leU^u3WJIStz4({jFOv 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 z%0D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

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�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

81Wqb<71CW_;{Ky2MBiI80naO763&>1l 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 E0gsN{S^xk5 diff --git a/ports/cortex_a15/gnu/example_build/libgcc.a b/ports/cortex_a15/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 zcmeFa3!EIqbuL~#J^Mgfq*<*1S(fct5JrHcon5VjAOx?3cv^zAcgu1GaBo}Eml zbd>#{Og1*uH+VT(XDFp!QmVQ&f4ps_QgYO;Rcg^rrRtu~A1j|!YNAKe!%Dqb$NOGT zipR#3cU*moQaoCp@{aD;m73@=h&Hj0k#m)r=`rFiV#;vJuDREozJe&rosY*vcL zS9c!%@t6Hd*~jPsr6zhjjlRL-0P4=;N07}U*R0e}QTAWlr__HWm3sXleK?h`DyQak z<-BXLa?ZHMJ63E{4v$s+-tj)9;c@$e%K7BE%K73`%GnoJ&Z8TZ^W-DS`8(wM$5G|H z(5alCKd+qs8db5%xhgj2UhjDOA{9HWU&WRuRP4NsDt74t6}xJ!igoN(vFk5Zv77Hx zvD?!s_NlmveePKm`x4SWu~@}^vQ5SQ^Iqkiuu-|mM&+J!hj(1?kaF!~9l{Ui`1Gi9 z?c?k7lzTYGH&CXZRkpMYclO*c*t1Nvw6t}#wfD3P_aLBov7R@%(j7Bkpu zG2Q(Z(_=G(Hq$$xTG|G#YZ>So8Xo9VEj@ihT?0369r&PC$dp>Twqr|M-{vk|$*?5# zRuWV_EJ=fv1Pv0FWSNx&%@mfT(Mp2G3rn)xN`fX0OR~aBf`$%Da+Z|@%^#LzrIiF1 zB`nF=RuWvQkR-The(UlAh9tqI^po(ih9toS_LJ~pha|ye_mi*-gd{;{@RP7(wDb;c z8R)cbu-@K52Db%++awq_MgDd~YE-DDuWNH>H?Fw3Njt&knSsIKc5wLb?9S)x^g`P@ zI}1X=BW-u@*0!PHzQLZ(ZePxDPlF#-5aES)Z5!(9>m2O1%h8?BG2GW#AgnhZRS@BY zUpL%aP%7{!2S$T}Q1A%dJmj5nbC>7#8ot-?9oO}H5q&10>w3G49i0P6(c9(Mv!fGy z*Y$<@I zVup3p(7pk96W*yzZYu}9= z_O&N(UHd?^j?G{Dki^!tXY6MG+S|ooj9mLc``UNg*FF&AU3;${y%6AcnBfABD%4ZG zMt=tbdcF3=zc;RMDCuFpgiOyf?E5?soejgic9-n5JFCQWc3Z*S1vxNAVVhD=ER*8YyI!+k?0i?Cik;k?ZKZ38_+H<=ji_rf_CYDIvn>v;&(jISW9 z<;E5il0zj^=%_|8oBM`aOlN4>($(AFHDChS6PPR?YP!xqS6e4@8umsMtjppV4INz& zZSj3K_VjhOboBSP^b^Nj;qh0l-_UYV7j6rS-w@!Nw_3g176TY0XS0LZFIYPxa(!FR zVAloPI=cFYdbVH7{d>y->mD&qy**eg&zH4a9z}6f6VxPa2 zkXbKq& zZt>Hb#*=i?mWAoywruX`XhByV+}hXHi~FWU&n6hy&M~qsXs{?W zUmU{Kr&gM<))`@FZeY{Wt#va@A2hd(6(V<%4hkE}7%|L}ZU#76(hWU6$esTC_4lgawC8%;r5XWV+h+6x`WQ9_Pdr1G3ny{?XlV`2|l zq!Q|Qr=7}F+I3Rt8Yi|cr_{Q7 zXHL4_ZAjPu`ryHHVvZNKJ&~^8k@CV+GM%F=M>Qqg9(p}2UxQSI6hOeqY^XQ>YImf|U9;iXM;d+QofspU)7En8JLIM7jt?ze;i*Ds^Du48LochBZJObKx@dfMx~%icD4op+ha zVi2#~&y+cJn%qx+XsP>)SnNEfE>&0OJ`L{CSmo}z26yHk=>)MD%JJ8_WXu`&FZNvc zCQ_`r!cr9xv9Mf~Xf?6%<+uGN)gi%X+R;*mHq`zZHZ%^Owwj~%znOI$P z*%irE8sKs2yIwgXPPIyuS37eYxAN_kr&iXKu^tO>=%!F{vYJ>UpOy~ZbRAXh z(!rqt&)p)vfv(=ALzt1MrJWodm$nZMs->&ezi(;V(9l3nJD>!3ChF{JAKu)8;&s`4 zPha;|WYuwP?d=0yH`uXl1DvN1`sIk=c^A^K=;bCY?>)ONQp2|+oPAAbV}-)~snjeS z%+GX{I8HUZcPlG(vE42v4vsn#2h*2Im-5oUhUO9F%@qc&h+kv*8GQ{RK1YusN!JA4 zu16>8!E&%3Yy+vsM))NUyy`!|JWh`xiGyLUKwdB8B@sq>lq>RD;g>w{s=q)73aiIJ zAl*|)mt+P*lu7EtVb^1qbvLtkAPLCle6J4)Qdl9dor@spCY#bsl z0YA@a%X=P;b|b=sM4s^C+Tn5a%Yzb5*DYun^7a|X#}%({o@{B1msasoxQZtHJh^`F z_QG7HZ(~1!iyB^JI2gRu9F%9BAM5cg7SDF*#=*4na4`MR7|s?FEfq6s0{H@)3aAvj zrFsSDfBW(HLlG9>La|&Jc6E(o=o3}s(L5|O!`bKjcw9RPH&#+n>&a4j9anX3s@_krZ_IJ>q7lP3gUv@mL6i& zMGpmW!8W^HkWSbjE-02Rg*+Hm`r&9|x-fl8y&_=^Wml#5IAn^=TUQpjIh` z3mPz{M!^LSL0;0xqg-j1R`{h1@Ty-xURXVXxS$f1Pcnlc$|MDG!Bf`t5IxScL0qs1 z^0sRU-k+3-E{f#SV(5%l!Ii=V3vk{cKUZNLt>A*XAIrF4F&ct;zS$}~`S)9V0{%`M zOncOE0rbE9w9`DrEI7I?XY?J2KY(%-QJv!&3XY8#dAROnoqU?&y4S7?bq6V^dmlrG6`h4! z-`6og-5Y15MZyMkZ?SYKwa2DVgBPd7I$3b?=i_J>d20vC+x{ zueuKcqUhddAS8+O)aB#p-rtL;d;5`&@`Q@%-dAwm1QPMQ5Ba~QJJ%V#u>OsmAVjGkCaM<;zia4j;2YK7I1n&>k(nJ?g zK0O>!ukJ*Bukv$EeZ9)QADq)3RlQoMR@JjO-x2B6&Ks`lY#Hk1ljtO<g0 zuS59r(b>@UoY#fT;kKe4lcaM#Rp9`Egg->(Ff?VAOV~418GlhPg=eQlpP?R0&rdlA z<9NdHl+=jBu7~(*5zcfTe8bx!@Y}>V(rGw2?v+XhJ4u1ApuDg#wh7@P8(y^*hy8$9 z;?705J;weJV^hUSb*9kBOHy!llj^z72l%8{v7K*w(`T z5BRAYg}(#-6D{Ph<9D*VeW z{=eWq+v1PLn7R;e5#-;B1X0vIUvTV+q(hys&B=mO@yyA2vsatwGovKdeTFZ9-U$A2wHq^&)Jp zA9jKc8$s9!e%OgR>>h-j=!c!8!yZD|Nq*Qo9rhT)=J{c7(_se?_BKCkz7BfZ1aqm$TxTCPAG7Ph z@t(x7o7EHC(`(n&#KAelPE;&i5ce?Mi4fieo&*FeJP+Tlk7_gn;-(iW7y20WU;kfZHB3xqZCNsDLxl*KcDqs23xVIFz% zFBTT^7@uFG;Kg4Pi0`#{%Iddx<{zg; z!kY3Quz2SGp~W-*3l`7#mn@#~uUS0fU$=P1SNRv+JAcCB8K1Ox=3i*>z9@K(2Clu- z5|}{!uLRG&%zKbH_d2-Je;9ugc;Zrt-wl5f2Z{0b!6nX=_`iU^5eJF!v@2hLL*j{3 zrT;K~4E*m|@&5_G^dH7^p2adrem+Z;{=@i1;I~`x?*YFPJc;q|1y8Ik`P;!u|6zO| z`2T6e-ws~-592=tp5vY5e+c|2coO3u2LC6Y??6`#X(>u(BY^yW9 z!%Selgh?vLkJ!7}|KXxPvhVu#{e}5(*_lQbSe2SD)h}Nj**@Bg^MVTIOUcywvE*p` z7~a)gRqv+PRacFys>izFV$X+%S?-OyLOti*GIod>%2(rzwp8 z?Rvybg=*nq^z|`fvtLoq!#L7uI4W@nmMoTTv6n_vpMHOV_mz;yYeInd8P|z}I$TKN z&P4$Az7TOP%R#&jueMu}XL%$Kyy~BDP#+3O+*$+M1 zC&aRm_-xibFCQi)5XSQYlKb_bEl%1PUUdNSK17cpu$t(4{nXVxK0SSf{x>e>BD*{aiS7SF(UEsW7?(7n|4g5M5%f zHN#~)vo}MRtTVb~-Laudelx90rtX@fXKT5Pw3Ef8I{f?&P(o-bs5w16507@(BA{-SYN+7txKkL z$@IKroZZm^FFosd33Lg^et(M|z5nqM=o0RB+T9?kq~w}_`sEFg-@RWMV&jTc*K(h| z(DxlUr;h2THAiW0xt^}BuCtamF6r!Ey1}y&n62gB<9JW9AJJzdY>E3|g){dF8L|q_ zRz$)ZzJ8B&;kX*KWr;n4@l$yXpIwh)wybk?CG&rBX@j<`V(EgmET#*y0W~39WbwyY0BKv+G3wyb-RE@_=t+B|5>dI{%^ zV#|62@}k(Xc1N^jwc@<*x5@|cp=5_2%kvMmFKt=uKP2HNHVhSRU8!}3H^%!aLvN0c z^K{{$Z3<>86+0|jRwwt)$4>N~+9>s0WSRBNn^Nn>H=!J$g6AUC83=nhR)5M^Or^5! zXf1XhJ=c8zCe-btyR$K@{B#p(XUFJW)zB?Y3j3Mf`eZsc`#ZaGRZUK&s@cs{sm5%T zT3&rW^2D33$f)M7KS#ZvD{I=Bi6xU6H&vTa>g0@*IK}5zlwXlcH9dX@WNwc+)wx}X z{aNS4?bzpyrrZ88mB^08oSNLO)VH$EiXC2fa$roQa${vUALcec(Q*h+PV zQ{I?E7oRbr_GPyx_GX+5*Ni#MSBzC5ESVV|L)@ctzMIZAoSM$oEWIteVR<@>uKsee z`GT=z=FTxktT)^d>vHq4$4v|_ z){vd!l&#~r&)JjMmu(=fa(1VPSh`@6M>i9@Ot#)N9m@l~?DGc8>F% zd&V&STYxq+1jV}Wn+udP9n7< zb}&9C7hA}^ba%vN$KXFX?Zn}C9r$b0P7MAS{0`h?a>E$9!&j=3cd>1rn>%{jSXHup zthy?l<#REFy*xL08=skdA9K?z=B3}my!3nSr5iKi7ui2AQUA^+<2?SVfR?POOcg zO=8Z9Q^uStFPO9A;K7%j@2o``(dSe{Hg?i3wgWDGI>+|8b?)uC+2}vyMK1d#`Ys-; zr>oIF(RUx8hqTqsg}bxvg?Blws&`^1-A)-bk_Rhda*)ft0Fi#lPn}ii+%jB8)tsj6 zEaGyXM_G!ci>*tbpR}%)w7ZdZB)Kn(wx01sbvoOIddFs^vj-Q>K|fYA4#pdDvubMR z=xdBPmY;r}d)E2bFA3+4t#ig=)%$ZFVjCoP^O>bC8_R@tF9>6QMd!F$?jb4vp1<9f zZ9aH#hMP?1_U_-8#oonp#7(B!#}ePd_29K}aoupeW0&sCtikz0Z4fxYje z+~byV^Fc0=Ox5ELhh`X1*I4&H?Q*X|hA-1D|CrRLdM}HYPsivCjY!adlnh_zC(e7^ zS^lYYRwKp}eVI;&f6rseM8`euQpT;-2@IqC%k{thTj6_A5>McYh83y02L5sH#pQbc zMM(u+;y7WDI*B{A2p}GRap*AvsY5X}tL4)aem-p1L#kAZis2SguBqTgi7-6lNT=Z- zUMZC>h^Z+r42w2FmdJ)zZ9%%5tN`%_G4)0$QI@0B^IXNs{j$jGyDnxY3(RBs$C;Ph3Y@jQ zb$510>s{3;_#bTI{#wgxP`4T1#vBDWFdMsM=lPt2==lccA((UQe&SBdWzxCbxi4lu zHTK1fYE9=pwc$${yycv8GI!R*=K&{O@?3dm(Cc;o_Eojv3IT6I0n%9+^m1z$074-NA1SEg7bm%axx!)tU0m& zbiMxYXUv<|{b|O*Tq$STM(8gtbZn3rDZ#FRP% zZ7}u#_E~So9PnLhc8;BgzQ^l-)4_w!?NuAI-^E_2vDPayZu7S@lFlhl=VJd0M+V`U zzt6;4FHFvAPS;jpA9YC+jN3a-++ysrUNj%#$}^wN0yE4gTc6Ip`vbRU%iGi09rfw# z{pan--d3B=E`DNP_Q$w(yryh-rLQN-w?Ccxd(L$bjxb!RG)C!-IjnPj&KBJI$HU91ke5s|p7%1$Za87m zlO|@&lQ)m@c-wz@K8f!$vn}!s+K&wDDNc*j#j=vrdnY>d-d}Y1QdW4C?59sZ$%A!=&f6B_ya~jYTGz{c@}tb# z(op)ZLLRWdxbwE%n70igjgVb-38QO=AItML?i<5-PO~Yz@O<7w|B#9A4)7BjphD(B zoD*1|gY5*52PfDGsvs2n5zIUIInl#-=HAd4{Tnzh;ARE+&rkSmJ2jfgq1j$`Mu}HF zm$utmU%)exf4(y}3(rVq;Tg#+==)iCMuPi7KUcx?6xyF*n@*woq}^20HE=Jg@GGG_rLdPv%2kh7Ed~0^P_FJHcV)*k-T?lVX9;*89yal86|f`9O&fW(Dg~P~u*GbY-TQ`JGwRcS{%n&+_VRT! z7E9O2{sUI{GSuUgknp<^zT+^$3*>}hoyXD8lY@2c!V);xl(pFz>fO`pG&sM{gA&I0 zOwI4k)HYuNASAz>K^iI){-t8^B6i$XT)n9Z1bA0~!temA<qs&wgi*J!>P$zycBD)Dlvrol$SoiB3 z-k-!Zfwk+A0>g5%9)JgaJvPEGdEiw&82rMp19gloZzY%{!YGe&rCnO#x8f5$kB{|R_%T#5e~_=_!m zVqc3tnep0T*2Gx+iNPUNsZ9#7u-czhsQ z$Ku6p^RdB@#MZ~-8H zm*Kvx1D&X?XBsZOF4Et;v-Dm`cX&6ekI}d{A~A};W+lV*1Lyfz2~&poRT-{f@xq9g zAOg!f^55_U=E(_u3Bw~FW;;yE+R8o01-43DTUQiNtVOA&E}#g*60DI=<6wzneQmy7 z59&cuEe_mPz783I-zLV9PQ$_RRW!i}{5DCt#Rx{@Q+*ACrQ_Y5b-ne9vrvP zV@Tr81#j!sxzK|wM=9IFi4YKm57-8_yakY#H1cpy`uN})_@xZ+s_%mds|WSIo$e(t zNoFv_vXX+&gwMr!sh>*O7Ai3)M6oSoa6zfBY`j7FL42?eHk>wu3)y9tFuHblX?)-f zev*GO_m<}Mq`qEnPA>e!hM*9ADmtS;r4944Py88Wch9!Y?!g|Th9c;pm8qnr(t7BC zcPR?=5Z6%I3AEqj z=&r}1yJRBG*|BeCPkG){aU=N%3ORLE843^Fgu`?R>=>_rWuT^_g9dV*2c1yRPud zxE9oB#nLTC9$TL=9quKcL|zj@#Lvj5aBvPGBymA~wke`M`#lJVqR(0*>a!EjDZ=Uz z)Mxy*Nm42Sx0pT~iKx%GM@|%dwg`GEiavV{>E23vV2q%Rsn6D#HWz+k<4}k`GrA|Q z&Ki1d#$LIo*T+z%$uvu17K5^^jjt&x5|HDX6=Or3>mVrkiVI&=Gk}mY?C&Gk4NsNYVv$ z*V>4>>rx1aqPsRm)LrEfbyqT??iz}yyP6{Eu6Yr47vHsOmqvyw?Q8{UuN{6Y&&AlM zsk=6s3J5>3VJKX88C_$kui+!pUqb`n|r+1v_9vwg&pE3Tr`Ux$C*+>~rb!@J?C%1DO$r^R5)$pZb;?E5jPYci%QAw)|T) z4%VyP%J<xgX#eZW8%)`Ua%O`&@h%i|Z|aRb=hhSJ6)(3DV$zsvBU|vHNrmT_;)32E(( zjhsJkP`|zuQNR8I@}lV1S0n1zuSV3bqY?FMq9U5DGZRt2J`z#C-hukIhP1O4q`h+8 z$sEi2m3`XQufk7k1Palwd0lAf*Y1hW4e^e;=vC^>lDhRs_aQMlFU;OZyT%)$YX*b+ zkSuv4_aV_|E240w_aWg89FfAlsR0b-Q!$;vG1?{>JAyi+Sh}FjV7e$eqZ#SA7DY(T z%l!89tWk`OtVgNmV^1K>V3&(`kXyE-ryqGu`_5HnuvzA5vo)vKes9-)8gU&$M%iNE|NhUKfry++; z3}(b?4`6PCy(?cf3AJOf<>${C#oW+uTeQVm$b;RLZH{^C4u77?Hplg-M!R$F zTs?w!*0wL)=QvnZ<&^Ev=~W8*R`6P)ozT{+753ENwd39y?_HSAy~j=DzJ@i)vtuat zJGWOSV`%Gzx6N@8{#At6VxJ84uIM`f0TfG45q(_~xs=3>hT)zDGTpsf+lB^vtOzT7xUbWW z8Sc$zDTrYB#IdfoS4DKJyZr3PST_aR7pMU1`Z&wNuon}nU&pj}6cXn4io!No?7qHKQk2{(6VA=ZIS=bwbxbrrHIUPm(t@Lw?^7sI;+xJ$# zJ(I0StmB?>PbU-G$Cf#}v&lr;*a9b=so77t-d=XZi{~N^*l|q9BaY7}xThfTF5{H~ z{@ky|G)^r`xDK%DZ}dBABVOA5&e(SBaThDAc7aX3IP4AZ7_b$wBu4WZJcq$iUxWK@ zhKEVjd}2`yRr7&Ofl<}`?T!1mkKKH!kNvy_&th^e+K}Z5yB@d}HM|zMC;e-2j=mPF zGtOu-<20mm+vo1fj$ki~lM!A!XGb>KfIS?XD$ETwWKpMv9jSEvt-$`$hHUHSxi7=F ziI0&cb$$}_iP}Bz?}2|W{I$-8Y;FDdN$#z%2mU?q?}guO*vI`6s$a!BeZ=)xhX0(F zp+-(-yT+{R7u#PomX7br^4vV;>v?_mqrUrD9^m5d!T&w>BVy{?&3T<; zxW3p^18qIA>-2Tv;wLdr^X6yH1Gu*IV~En%=#No{KSUZ{Cx-DleGXx*2;+S3F8E{h z-^*PC_a20W*%5yi7kF~;^q;uiGb`X#Yqx_FmM+t{nLENEhJV`eSi!a)Ru;@GU{`0% zGlbFtq;*DX_X2KQpuWRr(y%v4^JZz8gX*4 z#%Ig*egmG)Zmfs8hBl|Fr~S-qKCAV53i}Pv7&pPq2F}Ihn%as!OwY4cK8>U2r@~$X zc3ni*2lpCy7CIw^3R7pW>P$!7daB{Qp#d)e+U;WE;2g5oK(TZ@2W0g#4~gl*Fu@wc z*{9kG<{LVP_a|{pU~NoLX)pon!RfNpgZ2{11Fzx}jWA3=-C@g1fJq{Z^4K0CuN8h< z-hV@0SUvcB*G@MNl}{o}h%!liIPCLs&x8cRcwX5XfN3$X`uGf9y{GARdJM_A=z7@I zi{QJ7X#`@N_T^DN&nefIDWAur{B?Q^i9F%OwZlte0{aX+-{kEza32axOmMj=xbU*q zz&$2j_8Jg=Gs4+EyG(q4fZrD2N5E4yiRa^cLVIz{GyI+a|26PWTJfXsKX380!~M$Q ze*%9M8eQc54E#cipV$x_f?+lf0banAwz^0CG+-sm4 zIqba#$Xk021fq3pey@R$#MWK|jKy99`_LBLYv3umxCt}Hea*NDv=jFlSY+HTgyXr8 z9v=>WuL0CH>RtnfVcX-LSs)*K@x2%uXQAjC6X7=YF%iRPr=p9yTic(2hqN1J=eULS zek1l4IAp!l*X7f(8eoiPCO8*mn#$;5fo@a$Y0W?vyNG&@_dgDHIM(+TuAeN^h3K6TwU6^_r;YRKg$nPMtj|HeEJIlRAHk2SamI+t=QJa^lNYD6e}s2LxCV;v zlPI%?0KY|pt2kf9u?7k*;v?-L@KIcs$H)u z(-hWMc0FVSs71v%n#c^~KEI-#hjFCSaB%D`l`g2SC@*ZBZ$h}phF4?MkJDpF;y51J z+t#4z9rYWqOcx{BFcC`rLI3zk}wdXR z=B2A>aMz|$y)^fT_U1CpT+#`956;dc{i_y%^vn3uXeTmp3<{1v#nJ`GAEx7dCM4$# z+AP)}0mm94Nk`XH`CiN@0$Sm%z=2y>YYEB^+AMhAQl^l|6JA_9d?NRm$f2uk4ii$o zh5g=J!pili)BUp+?aFI3-9Opae|eDoOvu$OBOKevzEj9ct9faTTL1JO^Yp@~r8{V; z@w0w;D)K&;E4?cC<0qei#73Jl?&$Y2aKU-FJ?z;=FRB?+8~E@1X4dh_0fE8cOysVF zqV8{N?^(LRYvwXtGIuc$xmoh~7y5kovz9#01vinC)ycVvWPYPx0$-4?3oE3Fb@6Em z<8HeifN7e7ZLK}BK2;4|4 zTMBnSgEW6-WYH0MYvH%$?S;G~!YGfrlESUrNc`(C6=GJ_$?A_Z~x zGdM5xbt&Ba2;@bv3GGI~M z+&!_urVwu|Z1XKY9Md(vn$WXxPcICMVkoT_8X`ZVXb#Z}u3DFcy=EDnckh5L2j4zw z_IcQET-tZS^aSs>H(XDw)OuoEU9k}5C^FoS|N7~htSi`q!}JB~`H|g*VmKQ4zd;JeO9{SeY6kynT^Nz^~~ zdHIYzfiPPSFm2E-`4r@BN4OAJO?18C#p9nr!4JSoQ?IPmrhJ}Lt{J@QKIj$da3PT= zytsCFX}w~ff#;jNwbd*y^~zOJaJa1Z#73*)^Afx;b=}rZu1W42Zt3;7mMvYq{apjP z-@Lu*2y{@R+4?uK4ytePl9qlRFlBVm-%snH>xSCe@!gc60ngnczPE}F;<{CiaZ_Cf z&9&+Qudg#Es)OWPVUw+cbF-W|?`jr1W#?WvP{s_pEQ`je=XHQ9Fy&3DDYiIaQfgS1tMJ-J?r0$vd#` z$fk6*a@%OOY|9hb&z4tZ?kz9Re7d3{b8lH$rlS8d8E4b&Cs#amU*@z;U(8fBrL$#N z8yv^;n6kv3kmJpvoN6agJ_rA&&Z?Q|qG!hzDz|dx%<}+>TzF@B-uQ{|o-^}zW?oW* zHTgA;Q^Obp03MYej~w(~QnM1v+$+E`sbMQmLSjjJTs|q5#2cAN2n@Cq$=*}t5^ zy=xcI`!RYi-;RIx7W`F~p@o?I)3<@74xFu*CX%y ztA>?xe2iJjo9~XVyE|TWgCDT|?)Zkghc*3KF%n3^k}NJ)5_vjxB8i>Y~f8NS>cuwzP3+V{#EtP*>Yv*XhYc z=Wp%o+OTzaprdQxrsS&i?@KnMQp>Mjwxs@aOD@m3#yjPjtAM12JBHNYmaPLrx-_OR zIA03o9jaGrt{U#^+1lp`C@52Ovxi)Z4&@8b)iAB#`E?Qec$Ttr>u@_>MpPG_fBrei zMX2-XhgIcO??9WI)jb>)%FrTbZCvxdOH_HR(mAZ}@O5^z4{vU1Yi}Ruxy!qyqtossi-QY{X2W`8bx2Lml)94Tl&FP3gG(n=oYW4c%9F(h&4ffqlc zhQRQy5)$t@2;hh;^bE#%zB8(?C<0lJN*tHrkUa1zeqa7ZdJIV%b$~7JdFVgpr#wCd z6nR{yE_vWp_d#BY9z)W8SA)0Zy#RT{IF!e5kw<%_E$%W`QY{C z?SNnMP{zMPUXmU|5_c0Uh9b^To)c^)75hf&Y zUqS$i)Gy&TBOUdikfaORb?!yFzm!C9B^_OF?DZn=MWxse=7ObdhxK5)v#vO=PY%xV-ntUtrv|#@%3C-cyWkGj6YOc|SA!Hsk(*ad#W{v&OyOxQ`n5 zr^fxYam&%%Ebk2CUTxeD821L_-fY}k>EgVfFz)A#+evwlqw9}Lrt1%$kUoY#aiyJF zuvdF$kG!E?dLGemR`mUW`f*)A1@nk_(~=h*%m=XVANJ#E9aG89F|O0XYLZ+G`z|_( z)pHW&8OyOaKbRRdKfuhyo*(Fmip&p8+-x%ssONR^ngu?k7Hhv!zYEuS1b;FQU^wOh z9LF#Z;Lrum1hUg$9`FjCO7|0RFb~i@s3NXAoHq~n5F;)p#&$W>1N>710Q~DgHtxO+ zB>?#U1_F07A_V-Ofp8oA|C3H9;GYAu0skL_SIzh%`~mz^;!NzhuADmfoK=pFg_pH; zH1=;|P&r^ekh(_ttC^nY{(O22xL31zLdDWg3Kkz#&X#Y4;&9$iLafD_S#fSLW8v@R zV0l-!m|IGx=Hni7xi!x}x#aX0fXi{=wno~%?8$-L(?HvYz*7lmhv96y(+Lu&MF(Ub z7NpP$zfFuI1#xz(4$c2bI(>KNgB8>L(BdT>b*=ar-3W$vkC1rJK>$>kPY1vp0LM{` zs)h9k!`VH!;|38YB=UkdI}IEb1!q4Gd5c6CTxq|n;kV02Tt~gi@-bY>$KgxL0I!Op z!K2{pN38M@ZHc@c0ePoDUJ_wKa;_lG9<|Ch9?m|W@pWrE#zRH+i_7 z#*K`Hv7q&^u<*)MWSjb~5G<_Mq>iR@z~qm{;G!Qtt=FZ(k3M-0!<^Cd-NeUSpL#6f z;j%D146C}0hqaM?mI1);Ma^(+KyiKFrL_Mr0Wh;30)RR7h9Ueg0PrcuQ5lcxesfDL z3Zf0+JK>3fgcbqbgZ-;lY_NrMtzqTh$3N(JH(y3cgza)h}ywXBbwVrd^wrbq6KCyCn+w|oq z`>znv(&F7>I$0PtmfpYwhmDPm?5U59Z`?Z2*`h7*)8=Sx<$goV(Yk-4V``fSJ6t0U zy8{wekZN%-onQ;<6`L4G3gYBq>8OV#5A!kIx9Kq?ap!>-Kch|u!#hbxyg{7Iea@dn zqEa}SdkFpy5e8S}T?#+3l+cqF4js=P{VT{zN+!6{enFh9?d6Csg_HU1q$oJ~d8>Tm z;pAnIXO~av8^p;kM8wG(Anyzjg8I>Nn zfVtFq{Dz~4mo+xVZSb+*TD+{4!(a1?nS~?p(nNeV92kth>~SWr3_}U<^3NDo>D~zk z24k)!0bahBoM$lpFuk6^xF22^jNd`0!sRR)t3?(f2k`Q5m@JQ%IZX@0%T%Yf?YIP9 z{$nOL3r93wrisGY7KWE^4B}-IVBzJP@_6~?Hw7>M=J4@y9uVj8vbn|36yb|GY`jtc zm|I#TYiYCcG-8grX63hf0Y{i!+3tTBk2TfeA0x#yUPc>CHeOERoI<xWJvR#>HF+Lje^ouA6E)~c#Pw&%yZ=I0yr(gnby8%If+xIbpzdqwt29~>HbShoi zdjYp!r&AFpsx^zVchc*@?H+gmx2a(Pw@-i*kH7Wi_ne=C6vhsJX07+(O5BQ0+} zDww#SoU+3CLeH}6uWvqdY4uxo8FT3CaZ_+9v$ji8zf-r7h*#_mN7EaLceIVY_)MQ^ zX=7o2Koh+N;Z(x|B5BeOcZ{EoV2Lx~r|eScQdS;#73Gc4V@Tw!1}}a_Z2`l(Oi1Er zZm{d|D(W$cFxG=@AoW-ezvO{et;3CbGd+eRj%%ZBdHZ0vWxXhmuE=YLU-H1Ko`F2p zO^E48AHZSDdj#?l2%|g{)t5I2zb)@|$U6mLLK3$X0k*uStn%?Wmz3|L@Jk-bco!~A zk{&}6w*$N_?|@Z4u7eeM9POD$>i8bWJ5hv~II??;AC9IgARr7ge+97+(QlHePas_s z%=}fPquo$Qia-}l<`cgI*N;G|EVei6L6>#Kd40MV7vVaeD{M0>xS%adV`lpdJRi@V zr$g5`%iZdwanaLo87y-2n&h+}BDMAc_+1d2T>S(zAL9Benk7A3B2Tn2+vLK^EFt?4 zL7THY?+~8Pii9YQ<~F7oJ1yiz6CbpuP$ zKpBI3x^Yv+JSMCk6ohC2i?vMgL~YQ{HWH=-i-b$xk;LY#+y7 zH%>pWd8>!%*ED@{ET!&wiCCZCd@olEGZl$bGi4Y~_1r=AkEujKUTyN;=d1wU zF9+Ud{A&gH9+gyTWP;c}?9I#ovRB#=uFvAZ_I~a6ZWl*iqe3frBjN$uKLsZb+h1g0 zx%(qJl`cni!1iC#sfgc1=kgEY-{oK4G>*PNtUdxKcFo7}@3I8I>JECda()qaTH%sd zCJ(3oJU#+{`IUFa0oC#SYA}-m#eaUBQ+LVI_tstV-b?DXbj11^&N9ZM8-pIl!KsbLxa_(N4Bfv+E(YTZfVgLw&I>5=rr}Y6;?wBc zd%^FJkH}3u^i3U!drXg^>{G-&b{`{tBn7eeV*n)_hdCxQKZix8J=O4nCLRKB6VsB; z#KH8X(xs3_^1!Po@Av63Byp?3i=R<07~WMv5=R|IJSD{E^^bsIJ*Y>d9zpCq3IVrB zCb%Lmh`o0~Ke1kvM_1&vTY2DB=i3CEy3ie)!i`4`nrLgxlRDO*wJ%3&+NPDF`WZo?7 z-KtB%Kf5$+ANHg?9Zl@b3qaD*Zi9WJ*u{HIT3jOEF22cxX}fj`I2l;^=-I{J zK%2O=KS>muIQ0@PF`H7~l>4+FYZ2i(qxcKh#4lxdh5M&)^58m#tN_=)VqB&B1?DPq zXHjy2P5hIL@NDAS;RViqH=T<3ml^TXvxdO&=rIXvn^0G1*rrvchUV+>XA%zS(ywzBpP^d1KmhlBpU2NuC2YNR5^tJW2w3XTe)F+1^k9 zzrnMoPg~Ka+=d`wINt7n#G<4ij*rg-?m;+p3_7=u2ZK2NCB)gpw4@-8FP1Kaw33JU zm~Mm~LlU@w9m- zMHpO>w*!6~$2VH#8xO~~K;9w|V&swCW66eBeH0apg5$r5bb=L`PeccC{68UG6dYfP zN{v8BDI9+XE&}aPLULZ=#kIrVEF7Pf!uYM@1-y|sejiQ|#fENQWA^V0{IZ7I&|3oW zy#c=HJfOS4rmj_?%mun_!}$@|*LhFc_I29lXkTAQ7agjZEQJn`1=wUB;@P)}E0~VhGKjT@5N4BLkRaABmM(?Nl817b zE{e_k?~smnm5|5?V(k&sgXvh0@v!zsAs`H61+g}Fn~j3CIifC-!oihx4r1*l$V-^= z!Rxnk5Nj`jyd>g zz_@xFljue6hwRTBFkQa!h$#=1rl1v)ceL4(m<9}cOX8!rA+fj0z}t-2^BWsEPD?lh#MUN4nyu_YT`eO_D7%XvArh@a`M!SNw_3`yKM;HiUz z9xPL8Hy8{r1$&)c3SROsE$De6#oAv4hUw*N zkpOMdFX?J=oK@m zkHOV?4DMPJuk{#st;gWDneZ*f)q^g=^=uukc|Pr{oKkBMr}fPw&(Np!P04*Zr}YhP zZqT&8@!DZp-}ql5IR^Dj@7A`V&Yo@E-sg;__09DBCjT*-BmLyc^!$b+py1)@`OWnF zW_o_3UmoAqJs52thUxju^!z6BSIt&U>ziqPE}1zbDqO|e&eg0X?xnVJ#E^a=8Y%Q>jkFQ z3*yo2>&w)AO7BxH7G8ru9v}H{^ZO`sN7Mr*-sp zwGFg%v~_Ih;);P%^P7fcXRpArX;ABoTQ699q2uIR-@)4%c}}S$7ajrvcTtfjShy8+wKK=zn;m6^8&Q|fRx|ZS!qvk#m$&%Pd*{wOyZIX8A2PCV2{o5y9`erJ zj~VE{bH~-Cc<1h4=v3$h)mTDZ3l1Agb2NgA`Zc+D{A+L=TIHNFehP@d2mbe$wRde+ zj$5vyo6EZTI+YXmxOJ*LUdNQJIAV&6Q4d*t9eML&dkv&MeVJ|vcW0G@Rqwb)T>EP8 zc|l%?MNQ)H8t)PKZoz@?3+B59cyEoWX<*IsPa2v20#jUJ_UU!wi_YKL*|lNo@IXh` zz)i_j>))4bNTrrvzidhU>FT%oODNuZa_qFW-O}z?;;%&-rjr%L_S$L$`(saVT}H8V zi>*A&&vaMOV~FP>1=pQ_5@Ebcge2WL2(Z_k@5K7whY(hS152&-y@DXc_unKByy_w_ zo%9%zI1UN6yv1OcpYpg$n(adV12`lPylO9)Bt3?t{b=*D6}&BPEf^`Ee&B(4%I90Z%)>R? zqyz>fOAYBb#(x2a$Y#1~bR4@cNZgkYhp2w0AzcFIot{_!OS<5C{LM)Bmm%qx-Ww;q z$Su5B2%HONHV(E6>&3c4gil*gxv=*D`~=1$Q0C;Hyn7L%X% z3gzha{%~(I{Kx2`6WnQBjw|HZ2co>ER<~jqFjk&Q;O%62noZn=(b1`boL2za9~*ga7(nC_b>h*BJpJPg*=*Vf+K)zY=Ct7CYm zt7UMgt%JkvVS=%7;a5vb=t+*s{s;NDE3l#YfSQ7@^l{)Yuk>j>4&-*$#x?J|M3u+T z$(9b@blp%}JKUiG&)p)vslPdJn7u`AUv@$9*TT5euoJ5>q+SITDvX|Emr+lb3T)d*Uq9LH3^ zXv7@Ea)*o6T80MNuIt~*7qTOLi*9-ibb+O4TyS!fQOFboAk@OsdkV$$DnV~p*Hgwwv9X5dAG~Df2 zzUE zZNuLoHtqjnAolV7ceu`vomMy`jdb-VtDkEHiw|<#h)-I3-bwr(-gAyqe`dWqbE$h% zcj~=()G_{^zAW{cS@DaxtuLQ>c`Zw;%|~%yZ$q!k!5?nwmeyzdo2K12T%tce{U%H* z^9^*QuRL)OnUH<^t+AQ!yHI`N0NSWX!~u5S5*saN;kHe6p0TOUML6?|O_g>+@`5W` z;kSu#q%(0)E0jvdIfUeaSGD4x?iG@_)d&zjL`q2FxR$||cfe{F)=%WM!!LO#<8?5L=rJVi`~mQ`yl25A5XSOhQ1;~w!f(r4 zhyf&tFd>QK^I2QoOIG=+5GUp1s3UnOV@(lxI}m5fd(|o*w2v?EGw@R$uOF!s@;tra z@r>iK$GBc7zA1nX5oQbj3KAfyU(%()yi*vsltl`zQTPVZJ?cg2e@RCdMe^w-K$Zjo zPe90cYZP9?MGZTzFUWZ0JY-AT!cjSQdsK%L?(MVbwR; zVmM?RkdZ;-$NLyrxIFa?1OY#Oj2SBQ4FcyU3@mpK(D6SB@iI8zM-FvipM;Rjeg!{{ zFmudtTH%sd5brw>URZha-SKsI#|a}{oyY`CBADDB%Y(_a9+*TS(M=21u(A&du@Z;k z$!{Svk0;R$b@Dvk6s?mVrPTLPHwT8kHX zs}UxCMt>Iv?11Jf`BM^@&(9aJ5U~7X_t2Rr3~=uHK5d~ zLQF^c01mr+uYgG)jPhpS5P3m7xiY3y67fQEt{|R#-6|i)Cy^J#lbedj+krfG`Qj*) zly5vdxefB7;K^H{IildnhmkG}*9Gz9_mM6No_swbo~%OU!_F)1F0Qmc`L{Bj*c(St-w#a3XZILTdk?~~O8#IdINhdXu zK~d5DH)P>z{7C=n2M<2&;=5wDoigHgAA)vCr{?f2la=pQm(-y7zQZQOAp?Wl_D29P zHKZ(a4ZysKCn#;8d=kl=5&pf;X@x^#Sv-unww2!I@^69jn5MMT z7;~z9>^BT!+I`C##t!}dSd)cS*w>jZ3}g1PgIXlIH;6HdrK1j$JQTxpHiDM8)q!;X zKX~3vLXs|sF}Xf=9}<!0T9-u?p3~2}m6{({ z&Dq}^JovG)1Td$;ohkEuTSr}y{T1-&|2=qczET^rzeGCl6aCihk>Jt4XS3swL4=oq zbT$r0UzCzNnnKooCi>UUKc{(62-B={%H0 zh?QD1tLjeY_SjB$)2syVSMLJ+BYb&`imt7*sZaevg%R zlaRy(G3p(tM-+_u0t9dz6B2nrj2iVDxvjY27mF~s(#{`%-^P?p76ydZZ)diFlmT8v z449VHWMjae(f3xhMD%F<3Z6|h6jZ2Lk^wDG34th4a#sqOE zN9a86p9JS^2{q`n!cSU>!^52et@LU;sOQyo&>@{^ z((R!Cc2w=4rfZ^phiM0GX_2gx`A*=Qw~BN6S^H6c*3KJD2r61r5Mwq)#F(^O+9cQ~ zh%t+$3t~*Bi-Iwy?Vw((2QlWf9n@3usW?VH8yptPBa9hCOc-!40-!J~R}79WrP z0Bz^g@NbuoM>);%@aV@|;kE3b9-p^^euCNGBs=KGouh6CBX~dkNS?d@1Ga3iZ6&6#98)l2?x^X=qi~*6=0voiV+4v)Ay? zA(UGJw6vJ+34gdPR4xwV^Qoghd4FSY=Cnz4Y5U-yTG~0)9=ofswV`$&D~30@*E$|m z>NSSEQ<;u@@VPtnkxlxpMnKReS}dKu>n#CH7sV#ZfrxjNkeoO8+*z z7o%~G1)J!NCbV00|IX4M(r$8Ct$qliZO}>dKs(YTd`Zls?^mM+W!Vqm|8fWmM|}W8a3Xa>yzh zNjrFUHop?*sla*u73)YpUCX@dkhcqYuh4m4#k=$>xjy5hzLohI(v-np4*$#W^E@;4 zExRr^PrFyBCb*GP|8TNhiUOxSJoc^ezZpi&wuVvNm?4xc-k9Kg;4)NAj zU5|f5o^q723}t-6oD=1k@2Kl@D6^Lb@p^4SqDzxeF14Hf6S!KNZJvk?T7r4 zu1w_!&#@`@_>t@vP~$i?+=&VsP?bR~9giKV=)I(dtGX({n(rp)MP4T%u_Qe%pA_oC z>|up`7<&spj+NiVs;cnwSjRW|CsS(HOvt_h%|AOI%B`*v4Cgjj*gs?@-dv++j(MwW z0d5j+VD)AbcbJioozwYi{K;$rUd|ut7+&G>zRGV~@EZn}yFa5->D~zkvxy2w!fZm{ zk=|@V-?W$O*+e(e9I~JnSCq4fZy=8Mb!F@#r@VLTjS6ln_}D3`;taaNV!`e|>2&Lz z;|5i%S#Q&ITyL+6t#kB*V#Xgc`Fe*EXP$s+Iz#X|xCA;Hepc6cf7P&ZPSj!LSO$FM z-SMg$`~YvagyV;iWWGZg$KQa+w`=dL51Q7RePc>FO%6*_e%0OaO?Ss<-&n7l)n15a zWmmI4iqyDC)a)DaO5BI-)GG1Uh`R%Edv!ddoo>f#X=fHlOFWAl&m#3JI)`$8&zXb+|hyXP!*zkF-A38`~W>UL*#uU)%yi(7xG+rD(| z&YrsWy4QBYaZliXoqOD+Mdo*Wz9+=%zh>6FJDodXA9X(xzrAc{xi!yAqCtO(q@F$; z?|FjUHk7*-$1ON`DIdWh#4ujsEjZ^+9754&gIjtwZ#gSg%4W{cMPdR zUL{;})o@?W*1qY@DkgKbIMCL&8QW5r87eM~TH3Mox+7Xn6FPUa`z`dfrr>&-i}9S1 zbD9%yu-h^nAxv=Y_?XqF5$(@+&NS%-=NC(tvhpxL)6pa)B;r>iK>UpR6Wpb|Q-vh% zJOlv#`?Lx5NFtK;7;oVY>alx?_eX8FiQ-6ST?-T~Ehz`y% zzl?NGg`{J8s~f3GG+GjYz})(}@VfE(uuY^d!>bmdUxl5QaW+YL81`12W7>6fvCk{} z2#I4%lxG}f%H^;1eg%0K;NnqN(&ar{axXPpi+3;`+|!NA{zm>R<8oXee}Qq=8h3+n zuQqO*aeIxc=bp&-VZ+~U++D`~tZ~_YDgP(N{e^Lfk~t?mXx!DP7w5A5#@$61@w<)t z4dXsx+qbIe(5D-1k>upI=lJ zrk>^;T(ic!IfuPA3ibnj6ZYBo|JaN;aFe28y)8rO9ZNR=Pwc+s4O@rqt&xJ#C&rtLgEuA9kw4Y&f(N#M zw@Kf?2nb@}V(F->BoE~<9r2%##H~hv_!;+O9K4%^B#yey#&9da>_ZsqF&+l~90Y6= zVQ@v>W$@eb?EQAx9?~xDRvviO|6}ia;NvQ){qNno$!^m$-E8_NEd{o;h5n=6wt-fx z7@$aj0_i_mppf)0t@KYQEfhrzZJ`Az77JLBKZ~Fug7&98t%|+~qSc4K%Ht`|hq_6K zwD_+oDo>IAzTcU1_Ren7WDA9YGM~MB&YU@OX70?~xo76g%@6$YsJE7XB$gkipTzwDnhf9!QM4p4920pu0xz;Vc<4oG>O>A3{QHsL06@pQ*!AN-Yyc{g%RJHTKgnJyGuv?n+HYjruPge0 zujmDx&}X-R%P(3jau3!zxw?pc@r zEsF1h8tR^1ogJ(dLV0<*SeLsPt(Ho;heY>$=gf@7>u_xEqWNp)dvDXlJDusCZrf6w zsBr#uF5bZjv^}Xtd4;-XwWHg0rF&9WEJpWCIl3LtTsarczw%oTsZR1&sC%ZJ z{B@;!VrgNg@-`q16j{#Ee5~!DE8X(}$SX$od=R=~F}i0Ke4Xilg}Udf;44P=9A2dE z$+1dj>E;SV5N#jy{xj>Ib-2G`bx-%~;1uFjZ8QxSbGf_%4Bq1hU^KYVFR#HLF%%p(hA; z|NYGIjP`9@i9uc!G~S!rMIIFvyQx>6Mve#8gkwBNBk@#JF- zhpZcUZ#h?Sc#q>kvB0@cx97bF6#ujMD_E+*XB&#*?TnE7!o*$@O9RdqV5!DO0I*c! zRf1T+4y?nXj7mIS@o4nVWEn+;sU*sZD)nRc0??)%HyQAHdae#UY;8{pSoQAQlSDZml!gHeV z5NO!>z}AJOTl-GG65$n+{k{R zH*^uYXSYMm?+fj-)~(&QO!O7N5lDwGOtu|DqptuOeFf0yD}d7^+~_Ot8+`?EzR;Hn z~t`x}k9Q_%AUm!#S{<8SLO^8OoJ|cp@`Xx)Qa6Yfv?f1MVq>Hz+zRZUe&CT{h zb}g}&w%<_o(m@t$gMB}4=E6?$qcgEaH!~#RWhP8WXIh4*?rjeePrm>SjC?KoF=rO{ zG8=Gb+ZY}%2xmgCN!K~hT2}octMf%I(Rk2Mdiq?+>S#eM^0NGetytG;wWjjH$XhSF zq_x6ToF+rJ@{YsPWGm#iR_y90%IKwyN59$=F28V}@0l?v5Bf*9>u3Q|8AO3#h`-6$VF<+Q% z!%EX%o5iRw@19N4Z;g+Sju$(Mgg+WW?6DA!GsPaIlxhS0nPoV&HRr2Ca8I-D7cAvo zFUqaR;4c_iGK{hOUd)mi^GCeJzz&&qmN$n-eTLVV{29GJ1pX*|UiMF8M}K2%B#PXy z+V;cwo_|e6c`swT0m(QP6Lkw)rK6B-yBE(sW`<=@E;#J^7|nGI;j4m0!>*d$12jBuZtdM%LFaT>gRjHS-sA zvWgR7$lXG(=sDU0ny%~X*Flkd$9}E*3iB6n=KBuKlFm-obh-^)n}6Y);`8WC+1R;u zkh3*jlxC#?)dmnX&j&)<^3BGaT<0i{E9Q^ba?n=t5cNkaCXS* zeuMki2M_Oq?_0f(!u^2UAGcW}0jj)nfxGga>lu~{?@#3|aN+>Fa1HN6#1gLz3sH&XXiknY^$k#Mwu>4p*EEA-gbIqu%p*deD&mkF9 z)1jw(J>5-#=`r=tb=1q*qC*$pi9>Zc&Nq7Y+#JQt4dt`Nh3-&%7dMnf`Bk~o*VyN9 zZgQZ*z;bo<{P|YZm)CUoN@D&a$s+Z9G}0r<4>&>~+aCQ!&j(~5LI3FjrwN=Rkk2T? z7Ykf2@G61V3cOz6%>uUze1H&*>Oq0u5c>BBq4z%}{$~X~FZ8|Q=Qx(>ucjYz`VgY3 zs>RP*=eR*3%aMH4F&xu+_v46~@so;OubINQ6fS=pfwh^@?D6ehD%0X+Gi_+SsNU(V z^rzrQTP*X^3tA&ykoDtrZFq(-#;9wnZpIj+IWr+W2z(er`01x#C{J(5R(Kn-(d^yN z+p$JnTf7-#jpocYe!qf=zoxOpTW0TFIObU4>w@PFntE2 z4El!g-dk=4FPurSJ(!*KZDdiwnK#^Tf#~3cL=0Ze0l?tpzpPJ)H4uWqi+OVF;AJo= z@!(Tnz~H5piNvB0(`WbUp&iROCi*d==kX~BHsTLMno$I?U>E>~G&2!d4gvWD1DtJp zm%-0%d#4hfVMZ;HA=tL}C&Q=P_V^&lw!Q0sdS#E}58L*h0qB`wMt=az|9d!6VjkW2Uy@Om3Hs5C<6-tAGgOoRXf1LtB;pF8~(mu#vg`OPcW*V{4{`f1Fpj`w0br0M@~31 zJ@(8A@e9UIMcXusGRlz|cYMV@NF57zYDM%GW;z^aeIMTapqQafSIZ@|fl}O1Ty?lu zP{q*m=AU2F@X2nGJ9J~HZnwvU@3E^^tzP8mZn<1Bb)3O}XTUXo!GhI`uk^-*^YzPC zE?MRJmoHnn*!3@2joEyc5Ki!iLgwcWvGRR~X23-qozDf|&2*gHaT|8jP&=+4F2@7M z@r3DU0Le!`yHLtg&F}%>u45RT>2U$O<4c1_<1ilO^>YY{vO`v!eAtNZ@d;O_D0M07 z#_IauK2iwmZoIVt8NEp(0jj)nfxGf*@Sf3fA=GkN;KTvyB@i&2h&b}mQI~V&O@_P# zXx)vsCP6?kImgnwIOUDx4%<&d0toZR3U)SM#iur#ljkne_KX@3n2)s?;Ck1{= z;3EQ`6!?x}@bEN{uNqq%Q68ifDHW1?ZTP*&|1%62A>&5>Sfd@T4!yIe4>ZFKu zClkiK=C#k282P`Z@sf@TI9#A@HO8+8RyPUotBy~re;h{YB)5y#q+ONJR&vxv{j21&s?>3^}@xg zuTGsZ=&Dt)dIV6fR zZ;ZW&t2e`&bK1iU28Folxzw*+r+mkONPV$8zR}>(IFv(q^+d#J81-Dm8MF--8@M_R zI|&3=pPP$JJP2BETqU@mj+uO&1*ma=dQ;(1S?2*Kr;nnRguL@m&|>wJ6GiGVHjH^Y*<$4%|p(1oG6%zA(Q(=JWPOuAP}f z`Op^F?g$qPG&&~ySBU=u0GZwr5%0fV@V%yN3TG8?&f3gvCos32z}z-?$~)m< z0W7{ae{qEGlM@h>QYnXnb# zbWARD{*zZvul#H<_Trp1rfT~baygUrns6P4soF_#F3A#P={OudDOL-KV&s0kMDL@ z-ag1HW=?gDlRx%pn!im2@_M6UQlP2Rv^Id?mftl_{<@k|9eV(I4?rGev!3bB1K%;4 z82m(e70#(%j*NGv3m49*ehz$mKdMvFg}U(fz_&}80F{prMVoWOar4E@sm?`Z4b8<0 z%N8BF1Fd1H%dxn*;j_sW7rKtRFl#(?#e5$yP?)PW49S3q`b5AScr3zh5BRH0MlduO@tk5c>H4NO%m@L4J-G8QzBw zRZ=Z})^4Y3RZv2mu2q?sN}VwNT~OKW|G0Ge#1nC<*n|n=iq_+I*(Q~z$9rk4OG#&Z ztS4zpHD>~@dBUbq?82X znMf>ZT4&*?A$mJ`?0OWwGU6rCsif51ga3lD#OaWOu>`ZlsmjilrkuvO&eD`u4WBMe z;d7C1Cn8H@WE%p{rHs=L9!OBbJf1w@Q!xE=+IPd)~4xYqSQ zR^pEvolcL54<0)j-A32z`q`4gF%7LSWw^|krmOWP+5i#Ts448CYH^WH+SPqm&sPYy7*Em4E=O8adM4XoMCGflQo`O8~#FW?7_~#YKD`x!j zoRhz<#y>|OLn)-GPV>G21UG-rJNbjEmb+iht7{y}c=rM1Js^B(GQnL0zG+TGqPz;n zKc6kh?_4!X;rQp5;5&+f#VKFm_~#$s+oeo^%13B54jb5jN+@RhvlZ=XXeaULSI3M z@zQGX-!Jgs$4hHEw|g9Dyp*1l#vlr=`?Hu|!gB>zFkZqQ;S|^H&k*|upp28o7uY|1 zuU70IGjd}j^0@X7Aisg(5};Yfp7 zw8oGhsI9|{C7#MNhVZTB|Hv@(_-2U5Q^3nOyq7?KM$8N@EGq{WUeBWu%u6>6-v=TF z795>n!!R@DHQ_o8rjj3)_)8p9iP^M!AX^E`DI8aDRhZaH%p-?DYpRyoH-7$U3s3@I z)vB5i*VkoXI9m+EM{59Jr{fNcH;UgDY?x`4W!bSe>`1lZM9H0laKbTLl zIzO=3T-Q=735FvcLUae-5bw{cw#f=H@b$pUXGXw!%KASlfzyEt}amW94W5{~2v7?Yu8M zRDU4E7=lYr=pTk^B77}lh=a1Z)D-{C(n&F74v#+-AP+ln_{~x%$88?=0{f|qZ}tJF zf^2AinIZO<{BebAf8m%Rw7-O7i+AiV<|N1i><50Dt;5uU9jG)I+^&50e-BT=K!r~a z1}gsz5DrvMgpmA!$`DfGff)~%1gA2QSoEtDS2$3KQhOHr%SVC2{&EH>*8Vbzl-$FWnl&`{BCzwZRD%hze^g7@ zc}&CAfGdS-3a&wd0~psCUoAok`Ck9nD$k^2MmxNhLo(1hbhe#$MqzK?UBjK*cp6MI zw|`N=3&&wY@f2{JVfr9I^3h*74!aFB*RfBbGu<&D)NbPr#8)^DBVRui0$fq{){0Z$ zL-4Q{P^W0&IBYcTBZWZTM^`qUCS>#`6$YsC&IRs{OWb|AEDtT01x_5GUO56%M8s)1 z7mmZ+eYsHWbMgw;xQ&K@V#Z;MpwDT&u_M#`6^_HsF36wW?*_zi?{_)m={OOpVNPD* zIP5aWt3$XtmG=M$#C06@4e-s=KnbI_a2(bjl{E=;bsA+S2&k$?rJo1BV#Z-Bz_&{y z0jfMgtJT=R-Kd0O#$lXk8`_CHhh$Jqhn~YV4h!d9+}yAn+2TU?My_#dKr+RT!}uBu z$6?I>TX7t=h55om!9I)dUV)zx_=rHZS%&WxxL@GG+IX%-^-<1Egm^ALD*h(~9`v}a zaaE%^^^v1pY#<&t4ky5#m>xHAqOOw5Un@6$Lhb#0Gh^s&Ujfgl6E8Ii8 z6q$GsJiT#YnmVW3odsOu0DVLD5OW-%PSYyXjrk2omJ{`uUz?oHQ=FAL04t zY=AnVygnpQ>Cd9TJJTuIwYxec&-~$FU!96BTsK6WoO-Z2y&Ozc%RzptkmxJQ6Tew)O>c`d-<*9J-t%FjaF4-AxXkVh?intakuxX%j6mC7?Hu!DW)tqcxO}5V=c{n+(V)Un;SN=} zzmb@?#{V~@#Dlv4pu+L6ddx9Hf%fFU{L6u21n|6UE} zdYLb~0_Gnwe;Zro`TH$C-EIvQ-#jm#1vjrAUfJ0IJvnah_eTCi;FmE0Zw;6h2@b58IBUu|XL_X(tZyE3#nmf#0{<8s zsAAz##kgvH%|GS$E!}oUy@9T9j&rx@7i{xcHYILFz@$c`f~UEv((f%vGWXdK2P zADf;9ll4cJr92e^KT%%y2?VeXH`tk;`4B=~ zJ(Fia9;I;rRdk`Ac|Q2Q-U%Q1k)@n#LS>~u>Oox{hblu%(9XK>KsUa&&V;`D{s{I}~%5EO$Eg9h|E?+W71r%hNgQW~iJg z?`V6@3(>q$Y#WF`DDSje9Tw^cyK&CzjS$FoNB=~DeD3K#UEnlK!^tQpuk@VeUbS2{1&wbS4%|}g>A1e$h@0mJb< zg(`2~DaY^i8Z&K^n{eiPL#D;6%e2I5q3dFeNGWvU66n4B#`=EG!(QEIUx?RkY<;gc zt1W2Ul>IaG;uymBo$}2++>cAKZ#})3CuG?t-Ri}4=I%Z34Cb~Oe+5&Ur;;8Ez5o#F z#eALQ>%}jTEgqP9F9|A`NSk0TsEOVm9%wJn#nt(lLux zMeZ4JAYM9qTlD;GQR>OPg3A~<%j(JEo8!gzfyztu6SpWS;?^yH6} z`eV;LXYAKhQRaKbUFSWBxe>=(ol;SKi@!OtDYzkeeaXgBF9n*du^!0&-SCA9XIBdr zC`(=btfVZME=pfMq}xREGamZh(VPE8zYCvo^d&UzoW3*+IQ^;#Y}AGz! z(%{uNj7Pp=^k%Lf;8UkgQNBN2J(Ax^-vt`qYhCHhN8>?eSEx?q73$3|LmpJHZuREv zkjHyd7Zrr>Z#REDY%c*C^G9ume3W0PH}8VHp`feN{1xiW3CPp@u|HFJrt8+ofWLvf zl;c-<8$fgOSMB7lE4{fK4Pv+ofefO&?h^=LTn#c5>U&|`KZH<-XcRVYIrxrJ51@)J z)SGVtUom>~qbL;G{JYbepU2I=yOVSsl0h{;^c*g|d0H-0Zf@RKy_rd}y=vXm0jC~4 zngQY%-iaP*_x|mmf1P;`#`q)n&NfrS$J;XayoWj)M6^0%BG^YBB|`M z(V4d1(53H4t#6$H9otVuS|iY*&3QJj<(u?l{P&jIGa`>GKeMXRPx`g!?@CL&z&Sd` z`y7kL=FGI|;Rg4-(*XT@8JIsg%N2G9y;i9S}4SN5fwO_@*eT_m%qTvFYQ-UOEQNs_S!3m`^^Nwh$iy z#-1Nfm^LN&I$uVsMa}R|wbCwUZ`~9Q$xQCQ=49>Lf1(zxr!!l++q!Jm<8`p5&tubb zoaq7JKMq$M7xV+;4#dZIkj5b+`7R4odv%uZz(*CPkAw1D(@2D8V9KN96Tu^;Fc0$$eFz@*iRx5Fp)UDJ@O?#@ z0F{r>_NO-RE>;$Ze2?QDY3_sf#e0Q_oVyE!H!$Z5c)VR~+g^lR&| zsdSu^jd94~I5&01cWaFPeD1)UkRC=4ObNJtmPF_Re8rRF2D;(6s4M2{hVGlA$az!@ zY>ci4I07;wfNVR269w`ar~h<;(*({G$Y+P)^93#!c!j_Z2)s_`DvT#DVwp89LZ_QFq3E*&H3I;Jl4}Q` zP;%hfaU0uKQqN=Kk)t=Fr;$>(jTG}f%YBJvG~__zd~R}s=Wy%1-hZOp zt0>l+!+-zp6}_N{Vr>Bg;aBw8aPG5@i27lHrIZ4rwyCLLN2wq57~^WZUGE#??XZ8h z^?Wu&v8J?N>1rlcTLaj;(s^A`;iqBpl;pf^UWY^h?sj*Vehu<%xR8{+_iI|r@K38B zyDd@Q;Db?0d@l42-vHco=9vMZP(N;PnSd*wdF-7q^0C`dr+n;g6lWNZFXB_BPSKM< zaJ7Xf2J~6bs&MgDr21Wi$9q_55JH`(EB$y6 z3Ui<)YNI&55Y_yU^3SXv^Z7imer(@kMxVsPRSw<=9tzbfS=Q7qjsA(iH%dP?N&w`q zXL^7)3cO9=Ck1{=Ajj3@ds5&_0*$pB^h#7N^R3@iT*Pqr&HbQ#bgNG~K-)9ae#_p& zx9n4?D_T?O1<7|$$W-=Qkg2R*zoGKj2Qw{k%)gd__lPfLD!JBye!tez=r3KDX*mwp z$Gu2))FiBpn7W}Q;>~LDnz%l~8(CU=Th?!?ZAI~pB;9YCI=-}aV>(*9AR86gltbLF z3*}N?PskgBt7I-^&Tc_hFtW7n_N<0?)vxH@BtKyXaQGIWr{EoEgXTf3bi6i=H>5?Np@a$IowjAM&{0J0Ir};yX-U zX4{F7wWdH8cm|fToST+pM*{kgb1ce}_`2MY(Tjp9i|%JPmEsmNjX!L1X*#y-XYL#cKeLve~(RKg#+1TsgCB<{pz@ z&fqCaB^I=zJ+@4mo@seE)}XU&xbB8|M;m3kG-X5@+oj$U%arBGdmD~6lxvq;@tm+7 zv0ZMhZC#G~*0!i|J1d_!WVmh7v~6jNwr$(CSXy^|y2tjWmXf9g*)6#D{a6pA_0P7n zW^n#JE|+^e5%<>T)D5}&9J<}!XAkE-Sq>~ibFYn=7KYIum3#G4xZjAq-`F}nGjs49 zY-z`GIMRqIlvd#CiTLYZSnfO@NpS0l^f6qPH!ISRKmUuJq z{9tQ*I#cDRGeJ-9k{00G{4@uzGuz5gE~YKvd5D{GqIr`66(Wcm!Y(3NTCAYAR zv423>`{)mzHU2p*e)_cW(Z)?_-17;KgT^lPHLbyVYs@#$d{=R#?^lg8rYz_=CEhsb zmEpG!=u6gsNY*MG6k5fPBJ?3b;}_m|J#qg;`tkE@p=m4yEwF!R7Q-tLTPS2^!}A7E z0Q#wLTLtFZdqT>f-d@k1XCmlGJM(dr?#4KJFp4@A^2;`i1;`B=5uBYEnTDr`ZXqt8 zJFRqL#Au?iz$o3Z;CBed#E1|VgAdEi&;UL8)WXT6#Do7Jr6jnVa$?c{rtj>*j7oja z?#PHzm6W4?IID(5Xy=n+kNP=*lvpqp0Q)pHLQXsy<-yUBYbd9L4d`+5;CP?X<*Tj= zkNNRp(?NGn{oy*1I3`4TTmk9+w-B25)rDV?eTGkoJ+H#^=~HL%kBR!%0n)K(FkQ0N z^9M&slj)N3D|3Vy+oChKMSCsu{2|dVgClZlwAWhj4~;TyonE;T5r$ccM7YZHk99>> z@TfrMAq63v80vi^JiWK^?{552+w&?P1XlTF{(TF7$?xOOKR@{l!K~yj2`);$NH90~ zXM)*D4o&=d$({rkB>NFePo@ZFBu4?1BKNblMXN?5c{5APgh8|`v12)Y27wZsK0a_CiPrvszR!T_!t7}$Vh{p&27IhD%d-heMKbR_R? ziwPb0O$UF_!S8o)4h59o3-RjudgQ>$@diAVOZO8_x)%2a9E&=z>3+#cS8$WA;3i$c zO}c`cbS-YvwYWE+DJS`>j%2}&WWkMO!Hs0Wjbw{k$rkqp+(XHxB!A}Ik;T0us#l;+ zUbX=%P*WBi&>MANZ#0Oq#%%!$Zn7h|$&TPAJA#|+SlnjE;@%MhpJSCot<)9=pBCc4 z21VIYk7UC;>KtT-*U{dQXM>nJA4D8I)&P?4A9*T(9o~71fxR(v#+>`)H70)s`M$V6 z_V}6+6UGI*uRSZcXw0Z|aO{}$x$%>0&Kx^_;W*6XN6z(wiZMaIF~R%I|0k~<6SVth zjXfvW_TeSt$Bqc9@Lidp?5t_ygTZ5hn?8KjIq_qH9!t&+l4k{f=4<4u!SAlUXCsJ- zv(jV7j$1f39Sn!ip9ZP3($nZ4H6b#sDt=}BnDN1%k%@8ZgCBeABYjXbC=GAN7b8s= z{^qSW9?Af5eCJH57&^5geT)B*$W6hG(aj~BN^gi=AK%!6)A{HN;g+C`IdZPAx5HpV z9E5)xjAurizTN3f;o`fc8BC4Qt%Iv^WyZN@d!;U~@| zfJ4Dcx!J(ReE_tF9loW&Z^rc)!ns{r-J_sAi;JHQe97K`3F6YiYFx|%Wv75MuDX|q z<5o4@a^UQR)z#OpMd_?qwP?|@n$BnWb-gu;ah%u9GM6>uy+c3OUtf>j7=Po!nw05( zWGU@UnS;%IYZtm-qR$g!jNXZ1L*Xfw@NHOwpw3(Ocb zu3EKvQT>{$8y7<$zSCfqhySyG)D$}{$6){DeC)2|UP|j-wrHJuWcQNQ^H(f(3E_wC z!>x55GPa>_gnS?CqFn+X@;0UlDws%-s7h@$2)(_o3^^ zOE(Hv99MUIY4B(qpdRIY$RQ|tY60Ir(9^NEQ>T0VDC4J)mm(rg!)U8; zC2cV8DhNNVz@J;Xu+SSS`$X;f_O;8bvVj+*Lx4{-H21wJ3@fi%wsKMZ!p6q>M!c!5 zVl@c2xE=T)7(%jy@b42p*YD9^jt0f>X@v046F>Kgke)&{lb$Aof4TUt5kD0rh7Te{ zc!T&G#n1YTpq;YzBfzPz9?<55LNzugz!BsFObf&ur}49|CjE4Q99htBmXrXpPo;mc zz~uss%?b4N;%A>lzS{)eEATS{9})Pdz$XO$uRt@qh4@FH94Y5GLX_Dkf&8`{{ht!} z6+-kU13)7jCvd62^#Zx)hv8o!#PubCdxg&WVE9}@(B=tbeJ~%BQSO8n3S1&^wZLly za?cC-?hyE>z^4WNLg0%6|00le$aob3M+=-JaEib>f%60|6?lWdZwUOJ!2c4+Z?RE6 zCdCc>tH4S$AkvQ%I6~k|fvZ_BC=WUqlC0}5Hb68wbb%%u?45rP*y#2LV*{L2lQORR z?_0f+G}-B30~}5JUtOlntIf0}nzyv3y_@&Y9!US7+AZxr6r12Ku?a>RFdv_>^ZKV? z-0pibT2?wX!HJ#N1V;~Z>=Qd^6TB>W5^aK)Wh(n#1e@SyYnQ;cwOR&#-r5A!_Cgx% z5w1;eq+@$Qle2agQ{=N+Tsxq~jfx#Y{j>v?71&;k9Ak&DHbB~3N-_(wsnnKM+TTt> zzGzoSWVZC}lit!7lS2bj=`F2+w`~v1*DuoUhezVh1Z*RH5?k66bqu?GPYPukpiBqR zwgvkk(!oo_+OjA+@Ye4c?b;7vFT{F^4)(%5u8j|62p-rAXT#oc;jL(DCzYly%tq^; z%tlZ*%opt)FCs1Ex4lmt@>9#Yz0KsO1ZB!HVO`U%!TXyl_QiQ9$3pw!@`AKWw`cs4 zx+UHSK-8Uo9O9;$FKb18ww!QHrsbr`u-l+cY5!rFqmE4-((l(=nyFLRqadT@6R3}2 zPMx9+w4kdp_4;Mpo7&Q}{!D!~hH@CcMCwy*R3<)bQyIjM!KTFar11;e7URF(HboiB zSe9bHoIt!T+op9r2z4+Tw9f37pDM6h-Uq%y8{Zz+#>e`njSpphBFehY*&A}@K6E&2 zktlOh_NguHEc^22E$u9CQ~pgXhr5kGD&NXzMF4&l+3buA8l`Z`Z@wm^Uxa)>l?w>qA;)rKwA@(dMVJk!<)(90Phq z?St_=7fv$@%TTD(}Z%@(cKj z+?q&!4G^}(?-0yN{wKkO$)6F-PIB!6>|Of^W>)?kAX!Sy^49VsPt)`-syqUqY6wzJ zLAo(4!tyndVKDAp0{SG<&m>`PayG%70V8dQbfwIC(0BOST;cjTgp)cYYj`9&rG>R&+R zXshzuAXNU4LH}cdlI<4!fnZkh9|UufJ!~S0W<*JTl%9FXI|(kSdLN4AS(0h%jNBSe zeueZ4l8+m2H2EWX>ykesn4A0+!JOnD31)bGZie{Ba3wk90#`Y~^kg*w@2T%`!02+u z!+tsAVZWU5u%9&^lDEG#9v)BB8V?N`8V~!2#>4)h@vwhrJnSDD5Bsmo84vr1#>4)h z@vwi+c-TKQ9`?7!L#A6j1@|_f0j*Jthy7jSVf8%>9m%`1M!x}$@o>O{4*h-yH?}R| z_bA!okQ^Eht68QtU28D3>5B2N8vUcBYmI(3UBOMdf}3;&H|Yv)(zUov*WzAvV@|R) z`dP_>8_9wj$$}fnf*Z*ew~{UHRo_C%rX;Pw(B6?44+r)Sjfee1<6-swTz0I{&t^w( zlO4fLb_6%s5!__Q;x;=L_XZAlj#XlfhXYz1oI`w}13SVP4+qhBcqFaKypHk)(Rg^Y z7!T_}^8G>M0qmMkZ!xep)p+=|xZgbDym7%!SncLw8%i+DH64zOjQ8WGOdEUr*ik{x z^{^0*9UHvlt-tj8;G-X2VoZk1RxAbW{ty4em<-Pfu38c#AO<$Uaq;uhk>UM=EqJH9T`uv;gID^&%X5dv189z7X0?wCBb@seelH($5WAk zVn9Tx?D%3?mo`McYG6a0Qc>5H4e@*wwY4GIVKf9|ItUl~L^wP;;`F6XufA5+1fECw zZK%>&z}2zd*WlvR0;iGGZ3e<=8+Nv@C{b|Q4|yz!D~ED-;<^j6cRKPD;H$y45j2LW z;}jUDWtJmN=8MM-Qs#18oSsqFj5wM%@~V3V^yhIg&!i8>?Xy?@4QQ#bC@`J(wz4To z&Aww(OpQ;cPq=)-@#98#r*OLRHElvV1ShJ#?QM#DxYX%$0TWK1O=nMaaW=(g99fi! z=iRtMo8p1^*coXYMkL>AhoI=G1$^&|c^-Q|b;`$iYS%Wk4n~@}=sa*cUD_1y>V=oG z3IVU8=N8D@?by!Z2xIvXa{ilfw4eGn*t%S&@>YNdJZDp+&5rRkTQXzqPH>)6=3 zvMDx0-XtesmA9@y-X2FDZasG&TYzg`DCT9zE5@eyvQsXY6U@oGuRvaZ1g1b!r)d@1 z6!{k8K!^24`;V6M4&WLGs5g;`IRr(&C1kcQx`_~0j7{-zB<7Z%%6l3z@E&P2!tnPu z@ExTdK;>gz!IyK@n1gQ3`9ePOYg=J(Ip)lpKDYpJJJ}T3 zSH3YeMR~6mVN=|WVxz6^LBc*za>W0V_^BW<{2D@pKPvw3i2o(=N6|1DK8XAkUYke}TZ20zV+|27z2SN501e{z%}@1pZ3k9|gWD za1iSi@rDbm5m+m5n!vdNuN3%sfnOE)guwq4_`d>Q5a{E5&2)MR93b#CfveG;UzaVB zdcCuuu)F<)Cr&EfmbjZ8J6=OI1~~f(v1gF`3H_#U-{2`?Bb?>f2nTgyBh37>?Hh#6 zfcu^fv^VfCvNv!O>>!1E18;NfZP-Hz9@zRi?hR~*{eZT+iDJ8ZH|#5gw!5hXvcU7U z+Z*_K+DuTEe!+gfCkpIlo$dDn&)a&x-*()4p)Ig*zu#4F@P0qq0?{4k_WRKWh&Fdf z_WMQCceiuTU+@O(_Z#Hw_Zw)+F|^ykAPqZR@%#N~$9voE_j5P+VbKh?^!4uPb01%V zJNq!I&<%b`-QZ_-`dL#P_WB)8o1A$w+82&doqvNjHQhp7K8<{xZ}9s)g2N4d^T3zC z!SB7K#Dk~6h7EpP%a09yKcTO~2EVV8dfv_W54c3Ds||i1gd7}#^h>ZkLDnL6^}TBN zbXVV(;mh0A_W}sm)kldv$KWaSxyR7IIBGUs0rglOFvP)tGS5s)t04|{6d{Nq?)%6a zC9wQB%&w?~r{XOBajFtZ7}`TUu*CcMw-$euP5ATAulxwXtjaqGE~>nbU~c8Z1hXr@ zN-(eTae@mfe@HOB@}~qdDq+r6!ytwZT-1?5B9(u%Os^8udC5W0`#8?5H0HMpE4hx( zpHs;>OC(c6P*+(?FspJJ!R$(^;V>m7Id@vesOLkkf;DeG!6jbuEYR11o|p_mC1*jI zPy?u2Nqs+}S6)dOFq(Z5)MjLjp@~)y6Tf5d-xxf%ved+_OcKnhJc?j;fMgZ)%th}Ax zy2|?q=2SjHFuU?=1hB2q_Ou39<@YSte-TWt3)b?%a7&vsyF1) z?aZ{s9hWv;F_oEg1vlvmZqgOpq${{d*Wxx^i+k1F0%|3Tsmw?g+(;JONEX~k7Tid- zxRq>iuX;8mo02>T)7p1)*%4Ej$&TPAJA#|+2yU_?xXF&i3ruT2XO)O)t<}LhHLbl0 zh9jeRLGe=4+Bv8^?91cl)?r%vKV;SWJFdu^Z(4gU?g#f^r(LT*CiuS(9e7*blipdu zo@=|?hu45@c&~j2UiW+O_=bS}tfTP^yYfN<8CSJ(MX@yrVd#jW#axFeU}6v=j`GwI z<;%nvz2GSaUJWVgh5}&?b3D%Vz3Li(EXUP|tK8uyp1{Q(lj-mU#JG*1t#kOe!EY|E zc?e&JOC9G8xlykXJ@ak2)IAJ*CoblJvYUZ3uDaWRXK-n{e7Q{lpSriCp)9N_vs=#m z@m%q68{S*fQs&O{ty}HA!C!lR$3CWwxEn~@1|A(_8zL*`cM6|%(qZ1nv&TG!`Fg5V zn2tB``Pi^e7$$yw%u?*J9mllj3hiSD;!A^9<1iliRyqVlPc7iP1w9*kH+9N)G6=3+ zX&P>z82i}g(aG&sVSp;{+yZ$I;E)iO1IvX_%jFW_jDs%7xQBWPB0%M>fFF3{dZ9lI zKFZN>?VbwlV-F+U1o)V4SN5?-A#ak%CQy0nfV<`Lm?Mu_)%(~2T=N3d`waq9M8s*> zjqtnjo^r~CqYjmKA8?IB5#_MArHF{rutNLTGfw_EHL3D;0M|GaF&gsr5D}+gM#lk- z?TfAtGPPa<;U~)L`vL)sy90&nmY+s>8lh`*Ms`;VkaP7Ica|q9H*5}*VKDHC(Mf=Qdf$Z<;r*29J8-}sZqy$bEI9uQ{ zLi8g)68I~DgHfKOzf0hG0v8kFS|%_f^q&gsjdEsqmB0yv$j6@qR zCZ=oBDdW1|YquteM9`Ji7!caWhH|f6L#EBcemm~9;~AR`+;4|{CXoi%H|jDir;EMp zGRIyvx)Xa@`P+0)UA62@@?o!a{zDzNu`t`pnXO9lJnHXX)&a%ZB+MiYSf;RzbA2ek{je`COZZ>N|W9TDj)WI4Ir4497a%=9B09M2xi%> zakK5#IP80y%FO-}L6yJ8U-GY(H(sgS!4SMocS zYd694NxK0z{WW)$vdEN?m^zQ80YSH zaAP|Xx{sFE>Att7T)LeZ=eVTrfW|qKt{CA=x`LZ@1vlwhJl{B1gPT5}an48e zxRETlk!{$GD8|S!ykyn9nt`Eun zpuZri=DXI1!8o@Y#<_>Cy(hTvtkL5-+4DB+^a*L~dK>$5yXS2qHl%GRddFJ>HoP6g zUbmlgvezx%{aLS&FYYi@S*C3AOk-X}*f==5V^G>?i_=bDo`O?qfQumg$;dN`dlZgt<{k^45 z&~&FF-?8DnrO(ZDpLIMI#ueJ|8lduRMF=%NKFZ{a;~FM@eT=y8!FA-NE41Mqh>sei z#$i126|={!0uw;&_0*}1lRdI> zu^cFmP~}|$oN=^!IsvvN*QvY}AOg?X@Fszeax`4)V;N}feN08V3DB5sS2ny_$eRQn zbt-Qi2(G-jjy&9Y?mo5v*St{1I><{A5vO4{!tcsk>Xb`YHoWZzkXLBKYjpD0l?`tv z6+FLk8M>qY;Kbp09b7dH|KL(1y1agw{^@$ggdM z!5jN|McMG8XaE;=BF`ZiRP#g6;j-Z^%JmIyZdi_GdD(p{pPR|P@(r@#6}`!=38h6_ z+EzkL06ZoB=fqFFh~ZqT!0@f&&xqgH>=2$6e;+ht@((2hzuEH!Nd1WPokEYI^Ps;E zA;MXo|4KHzOVDg-Gg=_Lju1dQ1`%P#ZNmm`Cb+n zWj(`B{h4r(z>@^768J%Zn*?qXc&|X+1NWr(e=P9l0%Ld~Q~qkSW0nWqYqQ;9Z(IFU z^;ZD!gL&QA?#53XU-UVdTTv!(Ion;eIfFOTcGUi{EwAZ3HqObuw>|yvRd?o|_EfK!~9y+7CTwKbN znQn?0j=IwM_`=6WA`J-jrSl%5T(BCivuqH)i3g6)P5_KFn7a=C4_^ zdf9@tYZhOjt)_d(hn7uQhd?#J-F9cYEzcV)e0a4OH(8(pc^o=?YxzKYqY+W#PzL#y z5D}+gC&RBegFc0ePmwweNM;u5L|hS z(3i4YC=Wx%oV*KxGmd?BJ@1by05m%FF0*)Uy!H|TC{M$+oEL-Urn?;J;?-u-1)58j z<1md2)Z>}wDI(%j-WvE_dDl4d*iVs<`M436E024phJvO}!`S7!@@{s@#k?aBPI(-M zX&j(lE##$$h|{o7!0*bt&B-6{Tl4oYaK=gFqHBOWo|C4ID6g*yRQfVB*v`gb&0s)K zqw;a@Sux`+Jgl zDuHc_<0!r_)v;Xj*FNUIEpuNQDEN>7`@9G`2R;V}JhvYSbD@6(aD>3Lz=;CcM=|_# zfzt%e6v#e;;qwJ97szg!^bZKUP9Wz_NawRbeU{G>;Ua+mp?-^^!;s30U@`|odc$Y# z3x5h(K8)|2$!5L>-JO1W;)#=rUYnL;BS+KZ`vbFJxo<(cYtwjUxm}x9zSqlyYtw3F zZQA>twP_c0vNmn|8@M(N-$ZUfQy8h=+C_0;2odh%?sSb4*462nrOwWNH}VR;yUX+6 zxju#K)Ozq+(5XyYpVXH2OP#acr(*9q*RJubb*^2DHskxj-j=?2ipzN3H`l2&czyRa zcpLWgN!;CDj`dQ7Yt-(;`kun`ALhDeVPK6Kcyephdg8m#ke@s6-EYpcMNmhDd1=ME zr)sRJdk$-4ct*q&S(i2y-rt(a&Jp} z?!5Kp+E%u4p54BYYrtv`e(jzqD>-l7J|}j~71vaiqwR7%Ul4C<>4S7Pqbv%~nb=*h zmhR$%SWAcXt%vl?dGytWj1{_v+-yh-0}`B+eqy)_8TH>Pjp z^jT_K){i$G6T$bTkBMipF|V%6Gw1o-n=|LV<}AN69m#flE?;?de_2Jtg;}red)X6l z{(K^Zb#>$Ix;m`AvuzaX1R)g@2{uCR}V%>Ij73YDe)PoWq6+8>W!L7TzUiOgh55n8aE_NbX5iWM(y14M1Z~y#?@T6~lRz-N$w|`+pc-pr=yCOXA z+n-Yrp7`z0s|e5hj@;U(B6sXJR?=03Cx81FRoJt?nH{;8-Xm-S@DkjVE@u_^(<_{V zz_D7b_wC@KwwD;mBC^Yz{JDuo9p9Y9gO2Zl#Ql!1F7b-vo0a&h@mcS$ zAuVdWl_p1a&%e!&#hIUCyY*yPN#!U+m95NowYcqS`wMje<@dM1#{LrMSkQ(MuWyYkjKqmE$DT)lztm!g^X&C1mU3qcH)BDx=Y?jjw zTpEWWJ_~ufh=|j$Z^2KT?Tc;|<~QAQcNoU&`vL)s<9CYP@>6+FBNT1EQP{krk-4MP z1E_q=t3DeHeh>IsiHK7^LTxJyN*8$+!VVaO*w)mkJoPJd>D|xYyE$h5wbfW@9p~%w zANm7*e|R5W#@4yHp?nN>9rspVN5#I9^Z)O0@{20<&M5Hb+XIF!g2~-1k~Ema)l$F)zr2vPB4#6Lm&rwBCXA0Yf<@n0tJYJqnWLe9P7 z|Cacl5PvHnu3riKwb0G^;*cAlewcoxz@r5oFYsJK$e$_xdV!Y-{D8pC0(k@`<$akD z@qQxke}(>{z?TKaQ2*rbD{zRwT0-P?ia>r$j(l?jE*8kWY^1M7dnG>|3?A0u9gU{u zMeq*h`;Fo3VLChSU_s;j1%DHl^_sCrz@P-Jf?*ZTqX7O{t$(+5%`ZxIMiwd+X`PWba4~%~oUXGiaQXjW+GdHu>xK>^tR~d$`W(^~CKs zujwqn>UmuGnPv`<3$OxNfYsAZ7Ge>cnJg4vt?`TzG4n zj+(jx-A8cw6Y&?}P|p7*|3>-5=_cdyFAnB={&12?=WL74-4+cn`82}#XK#z1zbzUa zO16<3_&=j$48dpPceOuHk4W>&3L4*4U5zEn@I0+mVSp;H za4h)% zY|3pU{Q8b#cx^#=?(I5se&=sEY$-J#i8;W_%1-XulYseb3_@kq5hg{?gx<$I9&k+- z0*_=k;Bf-S6QWU^DDYyTzhB^o1b$TDorK`ON8pn}XFI3dc7Y9q;A1``sHkf3*NC6( zo#6-4kH@OIo3gH3{rFwQuesWda)Qh0$9++o(d_Zp+Lkx_AMZJX zd)}3RK6@KA&OZhmhV!|kKf=G^zCK{1>Akq55i7U-;|#+LuQVFnN-{3bA7fa_HjUnk zV-FG>&-i|wjd#3>H{FZU>h90s-wFP30LIu(uOv`o80)a3ehI1XHcVt-&QE!hFIl^C;T2=@we#crF=Kyv-ja>u$HpJI zVQl;hRz>fiqw_gb$8wrd5xs?FS1v@-Qn&_*pQXZUN=2Vr{7sP?g6pFjOW?1Ta2CiE z6P~!mzaescurX>3Xtt{VfRM4ca+61^7tdc*V?GX;BVDz6*_y>QMiI%;$TrDi4dqCN zZ4xLpZ_<>EpMTn%i08+td2PCT$I{M;wYkfAC1(e(%`ck2hA#k_A+tpj4i&go<;_>^ zU*~3xw}M)4^mVprzEkMV9V ze(K>G2dKxnR;C)WcnQ>maWIG<6zw83?Yto1Odt&B@yU zT;ov2vyhh}B2L3T48JSyHVEOHhWUe;B`5D=z!@ivi;i=IKd`9zXBe-$1sWp$9>)#Z z9?4QP-Os=a5sk|CH}DmsuXDccD;fz<I`nj}ue&*6dTxsJGdH2@Xlsd?@((%O)EVEcG5T5ap$kmSxqg;F=sMHi z`$;gLPwL~t0Ewr>e*z%ial9Bl1yJ8>^m`rO1x9nv>$r3MxV+DD{?6YPM&Ct!mm{rW z^z;$r12gkVSWAeiU|tCu2tmJ0{Cws}r+P*D9RlwY`lkhcL+IZXKifUy{aEPl7eCvY ztG|yHKc5-$PZocz_~(hgLHy=^@u!gOmGRb^JtfCu($P+Wcc!n})le|Gw*Z zB!VVgV}RXL;ib2>b6-FJ9p6iBNJOAxQ&-=g@LN4^dfWbV(25pZhW_E}QTWmNe&Bm{ zpK))j^F`ckk;ckskR5!!Qadp>Dg_6-M@ce zWO$}!QuEgKV*pE#A3uwI5g0Qal}=`(>AP_3Ra0v;eO=$)UR_%>ab4@n`}hAWQkF#h zW!fe+-%#=bWyyV_Z4ca73mLUjT6@X8`QDngUQL;nO@p^&CnfG^XPbzoZ+)IT42!1M zKaczjWexhwE^5LZ|-Fz36_3xp4{X|>DZ>kEsrmPt& zo_G9SFFoCSr zmI3bR*YUdRQT`XTcn!8(uv{MP(<{p&3^Ng}rQQWCxR;S-+?&%lrS(PJ1M>^oiyp%K z%*Iya^Crp;yv{&gUo2`mW<1q*GdtW!WUQKz633cQ(^fg=YXW5^Y{B z@|d{0FP^`$D8fop*spRG<_S|+)T6ywB__^kzoj9I#F-qm(W(f zjP`-&abxQj7$19J>eAP@>$5x!@$sB~{j&&50O$S0(ROs)+faLNdqeu(_8`3|`$Q%4 zw59!LsGlJiJNHgkwN6dnTT+7bn0_sAws%-l+Sn#*5HEqW_JY3$8`&;%z(>5P5D^FB z*=@sXrK~o6N2htkx!WeM&@0PWZl)cbZs#)CR{1PbCZ5X|quvenK2TPZ)3>(Qrmt&{ zX0K~K=a(q6MxV)D&4Z2ECWhV}>MU4cGsTH~$leiPf$$Dbd~ zw#w&hEBdefKOZdJz6TDvficFJUH?5_6X|9zkCzc z5@5e2=-a@<_|O+kc|a$}`}E_epDWssRvGW#2i^zwUYh3f!FR@{){hmG1NM5hK8kmc zwkzhn&yFo6D2K8>lK(S!FUZ#iktXl67wYnpkds1tVLg5o_dgsso~f2c`Tna#T_eBI z)TY*SdUJbCYIFPWy3OsQYd7N^^KtZ{Gh3$M_#U6_6Zi9=BlmhoZi^%L7r@!SMbMtu z-dLB+%bnmSL>ZelhQ8=UhLg4$VHFPVN8x`eb#FVv$nz2SW5@%)Jx$rahM(n+{I^VM z+}zG~lEM4REgQtGjHWV`h+p+PCdwWCz4?$3o| zuU&_5wlj_YtAg;3^WFV&Ye_J&bY{H6dGHdj#82w^ZmciCU?qyZX+3+=iy4yPh&%{7 zZQR*z2YQcQIiZuv&t44+Uj!Y8 z_?0CH!hqEJiB{PO_dZ5&iOp4bwtU`nd%u!gdfbWEUpD5+s+sBo&F~LEvgd?eRD<2~qDan@DwL3@Uq9Ah`=KZ?*vW|J$BtqyW5a+4u}>gIJjSEK5?+KBWB<%AfHQI*QuC)x zAZoTm&UyuC{9>d??_7UYkE@u-#g{`$WQf-TBVe4SWw(8!g{$N48gUQY_><1g(|!zc?# zV!{h4e4+J4d!)&}$ok?vW{`8S^_BPFvHboL>q~e&d2C#@zpDHS9GbP7Pq%{jrEm#y`%h$Vz28UhL_1i%nNwln;%bn0wllke#ovQ3T{p! z_m1+oyV9o^=wBZ*o0VcOFp=0W1V>^6Pzphe8Tdmi!92>HnIschDr~9j0b?esh>eZ`P@7}6+`NuHbM*0}^qkj$( z>Di>C0_cDcJCy+OH!dQ-qD zD!ro)R1rD7DZP$5YBu z%BzlYDbDSn;IvQiSLm)a!dC0X#?7ULtr20$JrU({*S-PPlrS0|6IU26uTNdd;9i?1 z1I_4OVkmSmGPxT@fPE}CEaZm4&R!=Qp>$a5!60(1{-nEyflk$N-K%g%zN?8EHEt&o zIWMzP+Z^^arfAiv#y`&D-*XuPovQKQLYRB6?)QtwZzgdc%ivUx$DC8S_v`*$GyZfo z)&uMUPR)c1JG+$oAag)ZV0(@YqVR=aaSrGrGNYR)<$Rr)^QmM;`Oc^8VNUgN7uK~Z z=MTEV*QBd)mqUbeE3R;xF(^{sRBhU*MPh1%6c*c;Zix&wRFK zisMjzki|Yfi;aQ@gvlgJ7c|*1B|To3G+EaSX1i+Ae?r82sJ;7bhpDTygkyE9T%+5) zN{xG#S+ukLf1|5Ire2V?Iv?nqmYVkel~m`KiKp5%U_8P1zB z2W54CYl?APi*kRb+o1~POrN4we_*y}IXWlS=z3kNg*qo#g~C~C8A^2>OT`;#R}!lhTpC48QD_hBeB4(xj=ugs>Eu z6;bNHIu0{=-IvjuCZpQpb!U3hA+NI}cqTJ?OBc;ydves7iWeg_idFyE6q<6l|F|vm zM)s*!KtoOlrPH+}m`L~X~PnMf}ekb`%o&(NcBQNCvN6&|r1utQK ze=rP7P98(Sf7BG_i>G2X>by>d8LkdvVajl&Y=KU3r4@C7rn|aw%YskxV&=FUN8D?a zDMPONU1aXms%hNm&G|mde72EFiEDgPml~;6S-N7&TwSrnS-N7&TwSqh(*(;+jx}o9 zI~2N)Rc|&zDKNmkD;K=U2(2{8LEIL|)51<&M1T;B8; z?y`GUW3+$Fy_g;0nsGr+Mcf~{dZ<`t?)1l9eP1rgZjIsY+is|(!TszDKa#lMzwr7# zAyQL<|IDlYq$V*WT|?F#5*avQJ!-KW*$1~VvtJq!RY6V&-FFOCJGmEBHb$qjJe~VF zC;t;#cPf;k#)Pl(>DPd|6bn(xXv>@#$15wJ$>O8n+xzA zPhWs)HLaUsxFNQCyAeYXFZsmWnjoexLCkG&F^^Ns3oOmuMhr#l^NIO>f|xrK#N1`Y z%(#gKxrHO8x;})h6uhH0co&wG*Oy@@hSX3b;CQEonr{swzOs?g85_Kwl8TjONN1v_ zgi4wad&T-U!o`u?NX&anH8z%eSs%_U$}R9_Y>3o(Phy{kaBg067<)CedXbQ~IOJuP zct;d8BmgGgkk?^dUr-Z6<^K--bO?|n!k;sg2MOh># zT%HHdDlaXBl7v`_$fle!FFn+p6Pa0Ao|`i}FFYNUDy#4=Dk<5!)JA|U$ zrUOdanr3<3jW~{IhIdqBX>mAQP>+o;D!t55X=#pbytGDeGtE&UuPfw@3VC0rnCX#O zxyX{0Ez1q(B78bBf)X-bxt z+UQM1`6^p)V|%kSd)IC7aA4e7C8d$va8B4et)y)61>xM%yrwsED!p+f-qezmlhI<~ z8EAfQL1W6vr4Z%)Ty5Ct-Mb#`Sy)nVj>_wSCtt-{_1$Gw_4`zE$BU{j!%lUvf9fRYZ9d6VkpEOFbt*}a@sF`m<- zZ4bD;oRDn0+v(+eVcT7*m(Tj>CbyT5*n7@Q@8v_0>G>REJRd%!`8=Tv^XF_2{*45ZL{BrNIz@M{G(i z%a|4<101i-HeiAdXjR7Z9F=UndEIg}PoRqRW1{as%y8lCL;mn0v@Poj zt5Q6M_!1jkVzxqET3r)8E7q;}*60tbK^Why{9#q?9f~tuW5Xo^s_R<1Vnu7}V;$Ws z?QM!P&xH7e?JxstFwBZG$b{JHSXa-gnBpXGnDA(OXS6!j-Q3x}T5(cm3$vhoRYz;A zJEk~cvw?*$KkN3DgwwCM3RB{~ubdeS8!GqUpHSC{!K&j@*1}fg|Lo_B6s4~ z3MSqR>G(C61e#?B;wT^>aW>sPh-<)Q*@WM<_;usA20!cep`#qYJJNPE6g$JkxD4DV z`842i{H(hQJp1sQ3Z5!oxvm1w`{Y9y8xpPpYygl+8j6^KTQ ziff~=FYH{a8r!pHt}MbN7jL>E@gQ=PGn&R7?s>vZqv=!}BwX$6nAi?n@6_X7Xb z?$H-4))}p@t8b2}uI~1ZXk#0&4p+T1*3{O{|N7Rtt}Yd8>FzLj7Dfvzm&`9KN|c_^ z16Qua=IGJZSR;#By&zg#QBymwtSq{qvZA(Vc`c-N_jI;J>lzwR&@Qvf{lQz^ud3@t zu^MEe6YXe*-D7^VQFnD){RNfPMXI~GrHch$RoC@3g|&;G7>jCpBW}C1s^ipxcC=x8 zYjl2Vd;Ql;*%r;KC@d?gjxMZRQc;M#dsojcP`ag~^)Rqhv1ZiUtUfNu4uTCW zUG<$Ut6JLXy4yR4w2K+2bk|1dtL807YTeD3m6tDH;3sgY7=!CJuehwJ5M8P()_GwJ zTMF9LWtH=4ReNJ&7cP;FEr|POsk(Z7x7doZc-66{1-iG3?25|TXmNQ}Sy6crIxe%U zK|iTpP!u%{tgvi~dkvTO^K13vE;YBEzC&1xCFsg=!(V-n1a6QP77`1G`sUO##&NbQHy^6g{+rN$JeTv_2@fo z+B86J*7~~ER@^qRH65|~?pQ;#uCW`#P5eI6mv-sGs(7K6wRbis88`IRtt>?xMi8`m zQSn(7(W2!GimG&{UodaUS&M2pSfL-96EhO(Dfgo(NIZiYj&Tdy;@u!$t*qAJ7 zy4ih=GBaQG)US+>d?+isWKA^T=Hxi0coQcM1-!yIHv-S=u2@}XeX}0K%?++iI?yR` z2gIUn?QLL$NB5+zSZgE9Bci6~&df7S$_v-Cdd~2-a73-d=!|12Vx4WKxj4`kS1gJ5 zQ+wCO+OQ!DiodkDx^~IDvV?w=Fvf7mmysskO$TrFYV&w8Ll&-XH%H%=wrFQvTN9om z81))-KOAbr=2OLv*+c~RY#r1BAb?2Mzp0Bg9clS(% z4;K^^PlMqZqkm^hQ*$>SoH7_7c@&Q``#d#CFha|SSG1@$x(J3vFza89?kLYO))Fk} zin>dhwwPyzkiWh84ee+COcR{H)_Ezq4|FN9+W*T~;A1%pQzOpmFj>~F0RR3ep`9pB zM7VFJrUG9wdj|16gXzhKnVULMXuJ|>7ql-GLX($n27cr}93S81Y#MkK*zY5a6%J+o;F;n4Yp8Yy(@5YWQs$cvTtZ&d1xr`r<0!_se@`o~ak* zv3^z_zoSx~EqV(Z-sfyys}Kf%d_T8V@bTTx#@p-Bf-rx+FU^CFH^P`NXTmn$i{ZCv z;Z<2d)WsG8w3W98e!sj;XwVf%NO^2VD{l+@etFH4l`8fHSnm}>#V$+5#TT1KFG^N7|X{moVI-awnDIa4f6Ww5ohH+0h)gHFS-Y(qMgwX ztYaL1F9_Q3x}4!{j(!FP57*7|eFuD#ECICnvaH~XyGOt`5T^$D$nU#M>fSu1cA%2u z5l$Nv)|d5XoA~9m&Q|I=UqYKN*bXXTML&d9&Xl8}#P(I0{R{QG_Jh42Sa z41YXn7hruP&j+oFbBk?V#B{o4x9T3wzR52d}blLo>N7u{E*!9<0b=yUaCp z(eAY!v8Y}elYk-b*b=%j)`S-_yI777^!|XM>AA+zpz@y<&+M z3-$_5MOYR-JFg%j-5Q}ew!2?}brKOb*$U$6SY{<+0?nONooX3fQpDa3hr zwqPtLQm2miF%j`u7<04cqr8|nWGymdu3L;}7sd*=1Zs7}QZpBJAtN32JEVOqR+aUa zbl_z20doZx3N990E*KTOKya-fpLa~ZS@2te*9cxG$ax_7?ht%L@JE9GBFK3g`CbwH zh2YzQ?+Jb=$d7E~=b3y&j?css1!oG*6RZ@}$B}@qMQD!0Ot(RBtKcny_X_?{@Rx#r z5ag0O)9d{}f%Amc`+Q*7xBjMD#WjH@!5WgkiypJgNdxAd@d{R&=u^|4}q>=8w z1b;8_Nx1Kr{v^RXBKT$ro+jbNf=dPK1v><{2vVh&>Axe`C*fL=4f(t(^iGNYH^EON zT;cv_dd}U5X+)$OC#W?{LFWryLBzX9jnH+1-x9n*;_nsuMZq_SkpGt8uO&PUFMZ7K zI3nUF3Qm^rJi#*s%LUIB>=E23c$?sTM96zk@KFhWPVjZX0m1(wg8vi242*{?*Re!| z!$3=8wuGN9xKwbZ#5W4AlJIW`ULkm$;P(U{BBDLHmmBkYPVjZX_XIy8q8z^y{G)`Y z z=erFtC^$_pB8aJrj-M;INU%zf&wr+0A=oU~A-G1cSMYMdt%BDJ_6go8NL>OqWXi{S zOci<>5j58`8PD>OE)$yNB7L6FD}?SAn&n~qRYJ2Iq_+wEu+Te%en#l`h2AH03ZLu9 zKb?s5vze#$PUUpU`&*&3zphzg_6(g?>rsexdgWt@j@X zzn*76eh@#V=RB18%@O)Eq4C%lcHiud&fKn^jtAgZ6 z>@Bf3HTcxtK=A+UabYv*!5$O)SL{ten1cOtFuiY=0;o18w*glGcL1?( z%*fw^_f_~)P}bz28gO8D?Vh{=Hs;6=Hlsg@EQs3;!{$}n_h-^h_$*^5eC7_Mp$+d- z`1NiXI0|)P+CbG+1FF}YKRH*8*^RR<$KsddMPivrk&PH6`*t&)_TS`ZI?ADqGV6Ai zuWrxNZdaX6duxpNeY;_A`@yKW3i|`p?mkM@j>et=qn)ZgZzswJ`&VuIo&;HxtNKhC z^nL)6594gO-p?V@kG(d$fzLvHyQ`|4OtK3+7+WtKLDNx zd(YQvV--L|kkY*mz{1{jc_^^78%()T546*6+6J>u_WJM| z>volE%l0?}GJ4TwOz&3Fz8UuTupwR_3=AAIYhyooc%2KtmyA72$VdK9P=@p!;EMzX zMn8L1Kl8cdm8-P;cz!#!>}5Y$BW*tgGO(JZ^EwxG+K)beHv31gZ`ZZLGXXrPx8`BJ z_Cr4Fg*xl&cMRg$w^2>d|q7;spEYF^PuI*uut|Z_`xy(AL$M2>6VO6GYncO2v|S zf;EGlXpNS$d?^#`lxbB>t88mkJ!qfI!kiGVK|-RT*46QCSrfR_YXLl8`Ir-H=JX^@ zs73_hkOJ3+&mIS;8|&3rQx8*66H44gr0<^^~G zSIOFHIGGF4Ny*3He?;=r3{Jie=~9v(M+mH$Q^0DhncV}3b!s1D9@u(Gk)$c|)6C|4 zeU=G~7FQz1`6^d zsKEWmXH=jF;c4H+A5DL+C)4+A)?Bc0zRx&j&1JMf)8Cnl^O^qYG}BqS`&J^sr~t41 zF@e#af%pwm8e>iP^!GF8{4sZv`BN@RIaOnBAd47ljy1-bS|ThUM@NvAUL&n^zADK% z<%UVBwic>jytDYYmCCJRJo3H^LdS8-NoPK2x6v5Kz{}5FlC>MtSuZt0Vp_ zo0zjxJ3a>j+-6!PIcJZhW{cY*WtwmoqUgr|9V+4W>I6&FgdGST$6#$-{bQYUiJG_{ zOxVRvjsFOL?me2j8f&W|mPW6(ooTG=Bx;gGrqIo%(0H@b2GRdBjCp+TDT&5BpE`~) z)cM&unwZdhL>rOh<(1{;=D~I&H#`g0FZm^v{^(DSbQ7 zdvRSOtUhAl(l882!rn_NLC{%-(B+eM$HN?@J|RuueGv1(>gWCz{}+H!%3^btT)qr<@;F=i@lp zH6^okiD3(K6f9;U;Z>no}4Cin5S)FVO^hVGjX#2q%bKCu%wjH6Z2|FLd*|XFp z#fgf>bb#OXV+?5PC~K%~2gCRtK2?~Q&mP=ug&Dy@+Fe}fUg2FHxGd??5u1}Y;ibsR z;~iB7UcHBQZN@m}OUH77EzZaCWq}4?A?+7zdX_iG7yeE7(dXfQftcASf%)t-LH9e;%&(7=fV_@8j|}-{G|02lwnwP*n9v`#hTJS}{GLo=}D5S3;?!PmNKlEhcEJ z9;{BTwK$~*u_AE|*}OlcLmgN5{d~&I3H6sumn@i%Gk)4vK^4|$h1!Z}o#4YL0XJiT zCzv4B>Vyuf;Hy=7KflVh*@&HLxQElX^|$XZsS{YBv{?8o#rnoS$)~438U2!>7}Nlq7if+>ZS5tWPMqGwuxWfX;>}htDf2ZExrXlgy4x#S=ZRhdi=R$;? zuakZmH0#TGI%$7L!hW0048QPB6 zKt%k##N&@pzYF&^Sd|N$ESM|!6~V=V><8qF3SJ4hE8{#Ajeb6 zxkK;)L5_jmJ6OMsP(vzZne-i3Tk~Vgufs( zU#}?tkAnJpDrmhIA@EFG2gd7d27zmZzD)4%1@9JoO7JBjey<90i$BW2M@fwVTvyWE z)Q^0l1*sd(@Uw*08)qS&J1{bQjo@{}>Ze+u6aKxh<)xA&XO3l^?lWG^ZVTMK09seK7Q}cPV3dE`VoIZF3w57_ipg=%mIGu zn%jT#-bMJ9Mc%&}Pvqdd=3$PawB@yOINuoIrZ^jbu3ERRH(q)JPNHxeKE-e`;Q~4hecSQ-LJKhd5*$K6`^pQ#F&tzb zZmO@(#p2$qug}C9CJuGUg$-;hmr=Pbv**mt?WoUfZE5RSlM6LyGHd(lc{RcOP~Pm& zm-3X;>m02zM{p}%FJ)rNi7C{lv<|nGt(6VZwTcs=uC=RRhpR%cPBhqTeZv}7(o_=G zwVmkm*w;XX22U?TA!B~JrLD2unm*R8T-h1B&>veTFQD;)9NvRRp}w5F59tc<;}xB)zngWfOqBh2q?HyHYUc__XJ661F5urkPp;Uw;?yh4On zKO@=SE{$^y#?3`o!g~q`mYelp8`yf(!ta-NC5XonW}S_zLV#c17!W~(Q6A-5c`M+z zY2a1-A~jq+I*0JB0AG*^#8D<)8-D(L3qWKdjQRTCFNeO*^g`Yx;IYn@iD>Rr6GQ#q zZ3x6`S6m+DGfy*o8omO&$J1uUI-l1DY4nGGk@uPThm^+h*j6!22fv@#-fiQ3H#36x z{!%3Jd2e%mefALgYoHlN$M{XashTo0zwPl^$uP!0Byj|`+bj6_>SyFC7&SlxUfVt5+lW1bK%FU0#&(RjKGgJ(&rxG z4&9Bh5Gy)7`(g0>v`mEKoEgni1^)r)X+fXt1L{XvzGsKfPYBIA{|bJ!19MB%+ljoo zrx$0+tcMQZ=%=sRl?)w7$ey5f7#&R3SM~Sp&e#Fo;H!JqN8Ev4bx)V44p0V zo`AERaH+4Hi*TGxG#YV7tA3tsWX_M?kMnRo>s13)YB}lyy*#zUb333fw+-bDZVm7( zC+G$4^7{IAsg6D;i29F0o263^5a+9%uG9w~V?4PEncRUN{fmH)4*6{d{kkvS3GT$X zCOh}K3crGlySz-$b8wbT&c^1Ls^T zoO8`{T0FEj^hmWH9_REovoG?j9JIy1@tQ!M!}5|B>EA#YuNTWfy|)|TSHk}a;^OsV z-Ec0=0_a^bEp$dd=*KxspH(?pCpQV{-4$qGZbI|OjU-l!u|I)qAj@S2mhHFnbfyku;fgp4pAve%}X%cz= zop~Xx6MTKonBa4J$3))PtM7xVEj-`Iqh4wNY11Q@CIzatxB;YDjx_pAwn%S3!uRU* zk=OT-cWmUjz2v9fCV3d=fiDw${&UcDf5ScIpbiD>TPsjs)Ct3qKKF{U*si*cs?WpO zEu#y3*Vtv5sPh;^I`FZ6mLF%!jD)V{E}Zu>!0;a+UY`Xs*R&@t*srFcY>wKoo9F59 z9nR^Pi!=JR?0x66&u+jOJ_DIsw+v8^IR*Np=&m1-pS;lFy@73*i*tq2BD@BD)K&cz zX$^+ddtB@I_hgmC?ZFMsz`-@eqW9LHn6b27>-2K#%MrAYYXh>1_ zVFG{-4QF1*FUROgcN7Ahhj4ql=71RQNd!Q_^4AQXF%2An{rGcovgzeC*vZwQ@F^!m zuc%&z;q`uiKd4@6lgMQ6Tnb4UaX(pcdd&OCMkW6@1Cwh|sHEh{_#ctX1<&N~WBwvOUPV%kba+A40=_Mb)e@b#IVl!2MYllwp zW8e&M`O_IZGKM7eH1&x#@grkmWQrddgQpqvH0_ZwD_Dm3kuhnc;z!1CZFBI*m;ev4 zkRxNXH_*p`rRPFk{K%N+nbk@LK|3!9Z5OKYhdYZhp&RUzrEEXz$WXx*T{~IPQ zB~@4LVnZGB$e8n4MtfuoyTrjq#+U&X-1O_tRi3d zAxIi?9s;>K>pbr;2Rcroo%6?Xp5y%Zdu)X&HD(j|Xx;0)tog{pMO4f8Q_J^b@Njh= z%A&km$g{}D_o?9X7j)LSfebe+ZM{A$BjgB-eu-Eaf4~s==9@sd6FF?V4N0U z&e*`&BXZ&fbL5E|W4JDTC4O$RE-fYS(+WzP%g`paf>Wcb=r-NMD%Pkm*CFaH_}xn# zHYgMDWDDo~3D79r%pj*~0&{XVlhs)~k<&o;GTn))v&K{M<&-=THb~07f~9fJL&MWE z<`YCy)7rgBOQ&Rh-7(T{^h>{ytXBHXTKaiL`YnUfZ`IPj3h7^_bRM#?fu(s&OQ+=h ztd>;x5nbWsV>h#mkMKe}XR}t1>U@`(T0Kf3&N;{52;hj6;y!LvKDR#zQImS|u{%F| z&ZM3K?99({P3oDW+$S69>q6`fZv=gaeJWdrA@*s^d7#SQqughYhR-?IG|qEX2z9DY zcpiDt9g7olUesY#D!2!McD8-2KAGe#b7cc6+s?90_y8+Um07ey5&peRq?5e+Q2&&q zQGw%-<><2@ZgLxZ<8TSxiH>d|whq6ZIp^z+<&JlBH>et?JIHuPH`tQ#Xrl?5-&v|V z87SRK%hVX|lYb*5cm_;iA4k_V#!2?^KPQLa`m1d#TC$>n87sI>Yr8 zhLad3zQRPkrl%c?Vi|93RtuWK5EE}hyLp$eC2=Ilu`OWIgtKgF856&ih3vEMM1B!? zChlj(PZ;ho6R%}Sh02`xWl9n%ed3#pd(u=kg%|4p#F^a_l>UbJg;((tHuCpx?B5X! z`N{fFJ=U$@l2BU|=EsgxIKjrngeyzS@~3m}{zy1B8=A-{X}OovNDbjo6%J(xJ?(vOJv5v*R8(w)9&=ua zR~hp1LS7a3zsKPMohiXvZ@smA<8ET4oY>|W4-493o|mWb}n--^)R^FHB`36Gd9j_6puaQ2lHSX-DL78%UFK!w(2J%t?sAl zK@0+~V8p<(VtswIBi4zHJ?mOqy4OZ8WaZ7WtzivlntyiYtNqAkD}Hz4cQ1a|zsj%K_ZRz!a;IgpjuzMdZNw(b*tWTS74}1KX=_rg z9eRIRY!!;VM|D6~LtV64?+zN(AA5{Ou-Si>v-57fKTn)r>#Yvm{`wG#z(#+!RkW_7 zU7~g!)oQ0bY_z5}Y=>>@Tf^N+}`tep1DVFS4($nu$D)n zFwAXa{rgWJv@LOb+iBE@Z5QRycY{@DV|Ah-qg}1Bm3jGdd~abO3??GYPfBZhrKQB1;|%= zKV(bJ(v?Y2B)0`d_p8MYxz(}O`K&a17LG>hsO#*CMOz2&8M!@l!yHbB8ZzQrbbp~8 zi?tfNzDaRG%sq=uvE@d#1?Qb&w)Qn!`ucUP%>_!_Aljxwg=r6AD{N|ns$<_~b068R z5Wi$Th5SQ&9`EX`_k9_sHymH*=RWFY^I^0LMhci~{I72J-(I)d|6sC@tBH6L`M>pE zoUPOjgz-7c=M?!cXH+K&jYp?;+c5?8JNrzVfk5&fj*pK|n}%{I@85j^);oU)AJ0?d zQ`tJpcLoCd^*C)ZR%1|j)&mdlcs;7&w`t&2C8O{y-xpxL6^Qf8yY?hgFUn*6Y`c6F zex@;#0O*uug77&jkFRd<$922Ox7>tjZhJjiK>72%3Hh!-81uz9qIkX+!*6B7tES;& z{As=bD{l?r{PGS!-Zq3$-ck73dTfE;ms~aA0~N2ObvEt_1o-9Mi^&q(gYwXnae24F zZ_~i5{tohj^oX-@T$=OCdl>TAgp^190$VE;PSx{c z7M1m9o7j5@UX>YC>H=FR&^|AtZje+4{z7vnpNjVEp6{}G-YKAP>rwKU@#br(GLUB;#-ZR5AYW8Y_NJZT$G zd3>L-wDl4H7P{&$`!Zy|eSHZ1t0DBKLul%vSjY5Su($7L)O9&^NPNi<+U{A)6^27h zZ>A>fSnHqZ`4af77tJi4lcIy?Lwa6nXSJFbd;`FI_fYd_?0-AVjP~FerrG56i*8^m z?~6nA--mm5P0+bNNPYegdh#6Sv!t%BD3tpc^4K2Yzs z3fv%kTLtxgs|e@92J_MS`#`_X@rw z_`1Y%ot6CWNcgV>N1^|*d=rR>=fW`YL-v+@eB>XnP?Sj7$>?cC*9>HHp`0oXiaF4S*#|Q=m=Ljwk zJYTS$2zkwdt%6^d`1OKY1aA~v&HD}M=xFDl9R`mSp*hYloZ|~ID2Sm-(;QzIK1Xn_ z;3B~)K|M}o8#&AmL!5F!Gd<}-p_z{Kc|xxcx?5<@e;9w2&|8Jp;}PWb3H`9dKPvRw zLiY>(d!Y{qJsP?rM=GFS09?hWgN!Qpm8DCsp9Z<`*x1@$*X>)3Oo-HLUY~yz^g za79>$CuDHs%RjKGz$#EaCT4K~x^z+%<3D)wgNF7VE^dF}1tP9!h`|b9%93CJacXU0 zc`-~^U^H<^3*!Ozwea?96Y`&JZ=np^qd%eFTtf#&D zM%v-e7Q%zg=MJ`k@LyTVAE}M%jF;|WM3m)Bv>X`BY2Kr zonW(|9(0lJLZQuPf|d2FI?NO2aQf!?b0P&t`n+joXU63<_un*}TEii{dN*{ep*N{U zI+3^W+Va{S1$|3gg$Yiu+UQtACz7^&o?6jQ{FBcpzd~eSDzxs5&Sa8LGcRGs!x4$f zgUN}ig(+#y5)3b?iCZj=q+Vk&3ZG^)8`a0Chv`Q~q0f#mvk~sx%t$_r0!jKY=>B3R zLfFZv+m{D$k^_8Xf|5Wq0UnHj*9a+rO8{H+?LalME`JSD%d4Pm&Yt-iIVA?Cl&&^%q;4GpnIs9NX? zCCU~3X$}+<3TBj+m4&CfqGZ5v*RKyi+B!C-z87@>XLXn1H+a|RU=EJb9W>29+a>6y zK=S0^=MRrR`wu&H>1w$MCuEwhc7N^+##i{i_GIQF92#mSXMe334X=&NXn2SCXEa!1 z`OD2|4s9B0R)~DIqa8gf+v-*&+LKxfQ8_*HudrbeDnH!($lu-cXahbD6u3d+Pd<(? ze#bbvq4T3d@v&RmG)zanZ_^{r#toex{Ws!yM_Fh2zKj6>Q#61_=njO9!Ht5ytjSjv-2aCcO!l_4P~U_p&6t{oQ>mN7k+treC5;6 z55!X*uaQjyucA8MIC{j{xIWPIa}Gi`4}2qSBxrj1yGIb9T`3x3xcTM7h=qs_VN)By zH^~w}D|+bs@;2~2GYlX3&G2g?-$dp3or21)ye>#K?ELb5G}3sJME~1-eL)9>|1IX1 zyyk;0u~c5dm1R37av%P?23us%dHPGDpFT$0GBczAKYnN6rVuiJ<@W-;X3iNSdGtC4 zZc0Al4tY)&I-P!o0#6|ykg|z--55Au=wiVt!DWKHH_4BuyvA0+F2RcgFA>}#_#GlT dhn|!HpOEmEh$!@SiRZoRqCc?z|Ecwv{|7^Je? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/.project b/ports/cortex_a34/ac6/example_build/sample_threadx/.project similarity index 94% rename from ports/arc_em/metaware/test_sandbox/threadx_sandbox/.project rename to ports/cortex_a34/ac6/example_build/sample_threadx/.project index 247d9fca..a1b15572 100644 --- a/ports/arc_em/metaware/test_sandbox/threadx_sandbox/.project +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/.project @@ -1,6 +1,6 @@ - sample_threadx_validation + sample_threadx diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..23bc7fd8 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..8e6f0acc --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,249 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr ICC_SRE_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_SRE_EL1\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr ICC_SRE_EL2, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_SRE_EL2\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr ICC_SRE_EL3, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_SRE_EL3\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr ICC_IGRPEN0_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr ICC_IGRPEN1_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr ICC_IGRPEN1_EL3, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr ICC_CTLR_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_CTLR_EL1\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr ICC_CTLR_EL3, %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_CTLR_EL3\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_IAR0_EL1\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_IAR1_EL1\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr ICC_EOIR0_EL1, %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr ICC_EOIR1_EL1, %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr ICC_DIR_EL1, %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr ICC_PMR_EL1, %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr ICC_BPR0_EL1, %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr ICC_BPR1_EL1, %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_BPR0_EL1\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_BPR1_EL1\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, ICC_RPR_EL1\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr ICC_SGI0R_EL1, %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr ICC_SGI1R_EL1, %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr ICC_ASGI1R_EL1, %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..3bfb4a93 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..7b437b18 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32 bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..e7f95aa7 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a34/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..52c9a0fe --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a34/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..44afb600 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,331 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..4dc009b2 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..777062cc --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a34/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..de100e56 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" + + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a34/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a34/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..f8db3bfe --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..ee8834fa --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..ff96deff --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..f0fcef26 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a34/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a34/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..9e60e001 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a34/ac6/example_build/tx/.cproject b/ports/cortex_a34/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..21675fd6 --- /dev/null +++ b/ports/cortex_a34/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/arc_em/metaware/test_regression/tx/.project b/ports/cortex_a34/ac6/example_build/tx/.project similarity index 100% rename from ports/arc_em/metaware/test_regression/tx/.project rename to ports/cortex_a34/ac6/example_build/tx/.project diff --git a/ports/cortex_a34/ac6/inc/tx_port.h b/ports/cortex_a34/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a34/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a34/ac6/src/tx_initialize_low_level.S b/ports/cortex_a34/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..a56c067a --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + /* _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; */ + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a34/ac6/src/tx_thread_context_restore.S b/ports/cortex_a34/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_context_save.S b/ports/cortex_a34/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a34/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..e8a7f213 --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} diff --git a/ports/cortex_a34/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a34/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a34/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a34/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a34/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a34/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_schedule.S b/ports/cortex_a34/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_stack_build.S b/ports/cortex_a34/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a34/ac6/src/tx_thread_system_return.S b/ports/cortex_a34/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a34/ac6/src/tx_timer_interrupt.S b/ports/cortex_a34/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a34/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a34/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..d801e51a --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/arc_em/metaware/test_validation/threadx_validation/.project b/ports/cortex_a34/gnu/example_build/sample_threadx/.project similarity index 94% rename from ports/arc_em/metaware/test_validation/threadx_validation/.project rename to ports/cortex_a34/gnu/example_build/sample_threadx/.project index 247d9fca..a1b15572 100644 --- a/ports/arc_em/metaware/test_validation/threadx_validation/.project +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/.project @@ -1,6 +1,6 @@ - sample_threadx_validation + sample_threadx diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..23bc7fd8 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..0928d14c --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..2b8a2d3e --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf9e843 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..b0d22c40 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32 bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..e7f95aa7 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a34/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..52c9a0fe --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a34/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..dd70fe94 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..4dc009b2 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..777062cc --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a34/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..67dd8a6a --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a34/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a34/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..f8db3bfe --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..ee8834fa --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..ff96deff --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..f0fcef26 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a34/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a34/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..9e60e001 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a34/gnu/example_build/tx/.cproject b/ports/cortex_a34/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..e5ff05f4 --- /dev/null +++ b/ports/cortex_a34/gnu/example_build/tx/.cproject @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/arc_em/metaware/test_sandbox/tx/.project b/ports/cortex_a34/gnu/example_build/tx/.project similarity index 100% rename from ports/arc_em/metaware/test_sandbox/tx/.project rename to ports/cortex_a34/gnu/example_build/tx/.project diff --git a/ports/cortex_a34/gnu/inc/tx_port.h b/ports/cortex_a34/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a34/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a34/gnu/src/tx_initialize_low_level.S b/ports/cortex_a34/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..42800e0d --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* 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 */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_context_restore.S b/ports/cortex_a34/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_context_save.S b/ports/cortex_a34/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a34/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a34/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a34/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a34/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a34/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a34/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a34/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_schedule.S b/ports/cortex_a34/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_stack_build.S b/ports/cortex_a34/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a34/gnu/src/tx_thread_system_return.S b/ports/cortex_a34/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a34/gnu/src/tx_timer_interrupt.S b/ports/cortex_a34/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a34/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3.h index 23bc7fd8..dfe37586 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3.h +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3.h @@ -3,7 +3,7 @@ * * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. * Use, modification and redistribution of this file is subject to your possession of a - * valid End User License Agreement for the Arm Product of which these examples are part of + * valid End User License Agreement for the Arm Product of which these examples are part of * and your compliance with all applicable terms and conditions of such licence agreement. */ #ifndef GICV3_h diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicc.h index 8e6f0acc..998d92b5 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicc.h +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -3,12 +3,17 @@ * * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. * Use, modification and redistribution of this file is subject to your possession of a - * valid End User License Agreement for the Arm Product of which these examples are part of + * valid End User License Agreement for the Arm Product of which these examples are part of * and your compliance with all applicable terms and conditions of such licence agreement. */ #ifndef GICV3_gicc_h #define GICV3_gicc_h +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + /**********************************************************************/ typedef enum @@ -21,42 +26,42 @@ typedef enum static inline void setICC_SRE_EL1(ICC_SREBits_t mode) { - asm("msr ICC_SRE_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline uint64_t getICC_SRE_EL1(void) { uint64_t retc; - asm("mrs %0, ICC_SRE_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); return retc; } static inline void setICC_SRE_EL2(ICC_SREBits_t mode) { - asm("msr ICC_SRE_EL2, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline uint64_t getICC_SRE_EL2(void) { uint64_t retc; - asm("mrs %0, ICC_SRE_EL2\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); return retc; } static inline void setICC_SRE_EL3(ICC_SREBits_t mode) { - asm("msr ICC_SRE_EL3, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline uint64_t getICC_SRE_EL3(void) { uint64_t retc; - asm("mrs %0, ICC_SRE_EL3\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); return retc; } @@ -72,17 +77,17 @@ typedef enum static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) { - asm("msr ICC_IGRPEN0_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) { - asm("msr ICC_IGRPEN1_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) { - asm("msr ICC_IGRPEN1_EL3, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); } /**********************************************************************/ @@ -102,28 +107,28 @@ typedef enum static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) { - asm("msr ICC_CTLR_EL1, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline uint64_t getICC_CTLR_EL1(void) { uint64_t retc; - asm("mrs %0, ICC_CTLR_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); return retc; } static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) { - asm("msr ICC_CTLR_EL3, %0\n; isb" :: "r" ((uint64_t)mode)); + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); } static inline uint64_t getICC_CTLR_EL3(void) { uint64_t retc; - asm("mrs %0, ICC_CTLR_EL3\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); return retc; } @@ -134,7 +139,7 @@ static inline uint64_t getICC_IAR0(void) { uint64_t retc; - asm("mrs %0, ICC_IAR0_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); return retc; } @@ -143,46 +148,46 @@ static inline uint64_t getICC_IAR1(void) { uint64_t retc; - asm("mrs %0, ICC_IAR1_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); return retc; } static inline void setICC_EOIR0(uint32_t interrupt) { - asm("msr ICC_EOIR0_EL1, %0\n; isb" :: "r" ((uint64_t)interrupt)); + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); } static inline void setICC_EOIR1(uint32_t interrupt) { - asm("msr ICC_EOIR1_EL1, %0\n; isb" :: "r" ((uint64_t)interrupt)); + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); } static inline void setICC_DIR(uint32_t interrupt) { - asm("msr ICC_DIR_EL1, %0\n; isb" :: "r" ((uint64_t)interrupt)); + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); } static inline void setICC_PMR(uint32_t priority) { - asm("msr ICC_PMR_EL1, %0\n; isb" :: "r" ((uint64_t)priority)); + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); } static inline void setICC_BPR0(uint32_t binarypoint) { - asm("msr ICC_BPR0_EL1, %0\n; isb" :: "r" ((uint64_t)binarypoint)); + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); } static inline void setICC_BPR1(uint32_t binarypoint) { - asm("msr ICC_BPR1_EL1, %0\n; isb" :: "r" ((uint64_t)binarypoint)); + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); } static inline uint64_t getICC_BPR0(void) { uint64_t retc; - asm("mrs %0, ICC_BPR0_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); return retc; } @@ -191,7 +196,7 @@ static inline uint64_t getICC_BPR1(void) { uint64_t retc; - asm("mrs %0, ICC_BPR1_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); return retc; } @@ -200,7 +205,7 @@ static inline uint64_t getICC_RPR(void) { uint64_t retc; - asm("mrs %0, ICC_RPR_EL1\n" : "=r" (retc)); + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); return retc; } @@ -221,7 +226,7 @@ static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, ((uint64_t)aff1 << 16) | irm | targetlist | \ ((uint64_t)(intid & 0x0f) << 24)); - asm("msr ICC_SGI0R_EL1, %0\n; isb" :: "r" (packedbits)); + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); } static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, @@ -232,7 +237,7 @@ static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, ((uint64_t)aff1 << 16) | irm | targetlist | \ ((uint64_t)(intid & 0x0f) << 24)); - asm("msr ICC_SGI1R_EL1, %0\n; isb" :: "r" (packedbits)); + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); } static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, @@ -243,7 +248,7 @@ static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, ((uint64_t)aff1 << 16) | irm | targetlist | \ ((uint64_t)(intid & 0x0f) << 24)); - asm("msr ICC_ASGI1R_EL1, %0\n; isb" :: "r" (packedbits)); + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); } #endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicd.c index 3bfb4a93..2cf1553b 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicd.c +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -3,7 +3,7 @@ * * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. * Use, modification and redistribution of this file is subject to your possession of a - * valid End User License Agreement for the Arm Product of which these examples are part of + * valid End User License Agreement for the Arm Product of which these examples are part of * and your compliance with all applicable terms and conditions of such licence agreement. */ #include diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicr.c index 7b437b18..d91aeb27 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicr.c +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -3,7 +3,7 @@ * * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. * Use, modification and redistribution of this file is subject to your possession of a - * valid End User License Agreement for the Arm Product of which these examples are part of + * valid End User License Agreement for the Arm Product of which these examples are part of * and your compliance with all applicable terms and conditions of such licence agreement. */ #include "GICv3.h" @@ -293,7 +293,7 @@ void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) uint32_t groupmod; /* - * get each bit of group config duplicated over all 32 bits + * get each bit of group config duplicated over all 32-bits */ groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); group = (uint32_t)(((int32_t)group << nbits) >> 31); diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a35/ac6/example_build/sample_threadx/MP_Mutexes.S index e7f95aa7..c787c3f5 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/MP_Mutexes.S +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -4,7 +4,7 @@ // // Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a35/ac6/example_build/sample_threadx/PPM_AEM.h index 52c9a0fe..f7501eeb 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/PPM_AEM.h +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/PPM_AEM.h @@ -3,7 +3,7 @@ // // Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.c index 8898ff39..17cceb01 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.c +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.c @@ -1,14 +1,24 @@ /* 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, + threads of different priorities, using a message queue, semaphore, mutex, event flags group, byte pool, and block pool. */ #include "tx_api.h" -#include -#define DEMO_STACK_SIZE 2048 -#define DEMO_BYTE_POOL_SIZE 64000 -#define DEMO_BLOCK_POOL_SIZE 100 -#define DEMO_QUEUE_SIZE 100 + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + /* Define the ThreadX object control blocks... */ @@ -21,6 +31,7 @@ TX_THREAD thread_4; TX_THREAD thread_5; TX_THREAD thread_6; TX_THREAD thread_7; +TX_TIMER timer_0; TX_QUEUE queue_0; TX_SEMAPHORE semaphore_0; TX_MUTEX mutex_0; @@ -43,8 +54,6 @@ ULONG thread_6_counter; ULONG thread_7_counter; -UCHAR memory_pool[DEMO_BYTE_POOL_SIZE]; - /* Define thread prototypes. */ void thread_0_entry(ULONG thread_input); @@ -54,18 +63,26 @@ 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); -void init_timer(); + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + /* Define main entry point. */ -int main() +int main(void) { - /* Initialize timer for ThreadX. */ - init_timer(); + /* Initialize timer. */ + init_timer(); - /* Enter the ThreadX kernel. */ + /* Enter ThreadX. */ tx_kernel_enter(); + + return 0; } @@ -74,55 +91,56 @@ int main() void tx_application_define(void *first_unused_memory) { -UCHAR *pointer = TX_NULL; +CHAR *pointer = TX_NULL; +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + /* Create a byte memory pool from which to allocate the thread stacks. */ - tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_pool, DEMO_BYTE_POOL_SIZE); - - /* Put system definition stuff in here, e.g. thread creates and other assorted - create information. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); /* 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, + 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 + /* 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, + 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, + 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. + /* 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, + 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, + 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. */ @@ -130,23 +148,23 @@ UCHAR *pointer = TX_NULL; /* 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, + 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, + 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, + 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. */ @@ -178,7 +196,6 @@ UCHAR *pointer = TX_NULL; } - /* Define the test threads. */ void thread_0_entry(ULONG thread_input) @@ -239,7 +256,6 @@ 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) { @@ -250,11 +266,11 @@ UINT status; /* 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 + /* 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++; } @@ -313,7 +329,7 @@ ULONG actual_flags; thread_5_counter++; /* Wait for event flag 0. */ - status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); /* Check status. */ @@ -366,7 +382,7 @@ UINT status; if (status != TX_SUCCESS) break; - /* Release the mutex again. This will actually + /* Release the mutex again. This will actually release ownership since it was obtained twice. */ status = tx_mutex_put(&mutex_0); diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.launch index f0856def..4aff966d 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.launch +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/sample_threadx.launch @@ -5,66 +5,67 @@ - - + + - + - + - - + + - - + + - + - + - - - - + + + + - - - - - - + + + + + + - - - + + + - - + + - - + + - - + + - - + + + @@ -84,29 +85,29 @@ - + - + - + - + - + - + @@ -147,6 +148,8 @@ + + @@ -178,7 +181,7 @@ - + @@ -223,15 +226,16 @@ - - + + - + + @@ -278,7 +282,7 @@ - + @@ -296,15 +300,19 @@ - + - + + + + + diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.c index 4dc009b2..c2ce6faa 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.c +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.c @@ -3,7 +3,7 @@ // // Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // ------------------------------------------------------------ diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.h index 777062cc..4d423904 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.h +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/sp804_timer.h @@ -4,7 +4,7 @@ // // Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // ------------------------------------------------------------ diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a35/ac6/example_build/sample_threadx/startup.S index de100e56..3952a200 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/startup.S +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/startup.S @@ -7,13 +7,13 @@ // // Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // ------------------------------------------------------------ #include "v8_mmu.h" #include "v8_system.h" - +#include "GICv3_aliases.h" .section StartUp, "ax" .balign 4 @@ -328,7 +328,7 @@ el1_entry_aarch64: // // Cortex-A processors automatically invalidate their caches on reset // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). - // It is therefore not necessary for software to invalidate the caches + // It is therefore not necessary for software to invalidate the caches // on startup, however, this is done here in case of a warm reset. bl InvalidateUDCaches tlbi VMALLE1 diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_aarch64.S index f8db3bfe..45445a98 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_aarch64.S +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_aarch64.S @@ -3,7 +3,7 @@ // // Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // ------------------------------------------------------------ diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_mmu.h index ee8834fa..bce62b54 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_mmu.h +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_mmu.h @@ -3,7 +3,7 @@ // // Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_system.h index ff96deff..a62d2a33 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_system.h +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_system.h @@ -3,7 +3,7 @@ // // Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_utils.S index f0fcef26..888892a0 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/v8_utils.S +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/v8_utils.S @@ -3,7 +3,7 @@ // // Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // diff --git a/ports/cortex_a35/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a35/ac6/example_build/sample_threadx/vectors.S index 9e60e001..7784f98e 100644 --- a/ports/cortex_a35/ac6/example_build/sample_threadx/vectors.S +++ b/ports/cortex_a35/ac6/example_build/sample_threadx/vectors.S @@ -3,7 +3,7 @@ // // Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a -// valid End User License Agreement for the Arm Product of which these examples are part of +// valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. // ------------------------------------------------------------ diff --git a/ports/cortex_a35/ac6/inc/tx_port.h b/ports/cortex_a35/ac6/inc/tx_port.h index a6bc8a6a..33bccbf1 100644 --- a/ports/cortex_a35/ac6/inc/tx_port.h +++ b/ports/cortex_a35/ac6/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-A35/AC6 */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -76,7 +76,7 @@ #include -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -85,9 +85,10 @@ typedef int INT; typedef unsigned int UINT; typedef int LONG; typedef unsigned int ULONG; +typedef unsigned long long ULONG64; typedef short SHORT; typedef unsigned short USHORT; - +#define ULONG64_DEFINED /* Override the alignment type to use 64-bit alignment and storage for pointers. */ @@ -123,19 +124,19 @@ typedef unsigned long long ALIGN_TYPE; #define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ #define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ -/* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock source constants would be: #define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) @@ -143,8 +144,15 @@ typedef unsigned long long ALIGN_TYPE; */ +#ifndef TX_MISRA_ENABLE #ifndef TX_TRACE_TIME_SOURCE -#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif #endif #ifndef TX_TRACE_TIME_MASK #define TX_TRACE_TIME_MASK 0xFFFFFFFFUL @@ -172,35 +180,41 @@ typedef unsigned long long ALIGN_TYPE; #define TX_FIQ_NESTING_ENABLED 0 #endif -#define TX_PORT_SPECIFIC_BUILD_OPTIONS TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) /* Define the in-line initialization constant so that modules with in-line initialization capabilities can prevent their initialization from being a function call. */ +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else #define TX_INLINE_INITIALIZATION +#endif -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking logic. */ +#ifndef TX_MISRA_ENABLE #ifdef TX_ENABLE_STACK_CHECKING #undef TX_DISABLE_STACK_FILLING #endif +#endif /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 #define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; -#define TX_THREAD_EXTENSION_3 +#define TX_THREAD_EXTENSION_3 /* Define the port extensions of the remaining ThreadX objects. */ @@ -214,11 +228,11 @@ typedef unsigned long long ALIGN_TYPE; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -226,8 +240,8 @@ typedef unsigned long long ALIGN_TYPE; tx_thread_shell_entry, and tx_thread_terminate. */ -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) @@ -254,8 +268,8 @@ typedef unsigned long long ALIGN_TYPE; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #ifndef TX_DISABLE_INLINE @@ -267,7 +281,7 @@ typedef unsigned long long ALIGN_TYPE; /* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout can figure out what thread timeout to process. */ - + #define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; @@ -283,9 +297,9 @@ typedef unsigned long long ALIGN_TYPE; #define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -331,18 +345,6 @@ unsigned int _tx_thread_interrupt_restore(UINT old_posture); #endif -/* Define FP extension for the Cortex-A5x. Each is assumed to be called in the context of the executing - thread. */ - -#ifndef TX_SOURCE_CODE -#define tx_thread_fp_enable _tx_thread_fp_enable -#define tx_thread_fp_disable _tx_thread_fp_disable -#endif - -VOID tx_thread_fp_enable(VOID); -VOID tx_thread_fp_disable(VOID); - - /* Define the interrupt lockout macros for each ThreadX object. */ #define TX_BLOCK_POOL_DISABLE TX_DISABLE @@ -353,18 +355,25 @@ VOID tx_thread_fp_disable(VOID); #define TX_SEMAPHORE_DISABLE TX_DISABLE +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT -CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A35/AC6 Version 6.1.9 *"; +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif #endif - - - - diff --git a/ports/cortex_a35/ac6/src/tx_initialize_low_level.S b/ports/cortex_a35/ac6/src/tx_initialize_low_level.S index f7843ed3..d0b541f1 100644 --- a/ports/cortex_a35/ac6/src/tx_initialize_low_level.S +++ b/ports/cortex_a35/ac6/src/tx_initialize_low_level.S @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Initialize */ /** */ @@ -21,63 +21,53 @@ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_initialize.h" -#include "tx_thread.h" -#include "tx_timer.h" -*/ - - .text - .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-A35/AC6 */ -/* 6.1 */ + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, 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 */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -/* VOID _tx_initialize_low_level(VOID) -{ */ +// VOID _tx_initialize_low_level(VOID) +// { .global _tx_initialize_low_level .type _tx_initialize_low_level, @function _tx_initialize_low_level: @@ -86,15 +76,16 @@ _tx_initialize_low_level: /* Save the system stack pointer. */ - /* _tx_thread_system_stack_ptr = (VOID_PTR) (sp); */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr MOV x1, sp // Pickup SP + SUB x1, x1, #15 // BIC x1, x1, #0xF // Get 16-bit alignment STR x1, [x0] // Store system stack /* Save the first available memory address. */ - /* _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr LDR x1, =zi_limit // Pickup unused memory address @@ -104,7 +95,7 @@ _tx_initialize_low_level: /* Done, return to caller. */ RET // Return to caller -/* } */ +// } zi_limit: diff --git a/ports/cortex_a35/ac6/src/tx_thread_context_restore.S b/ports/cortex_a35/ac6/src/tx_thread_context_restore.S index 72e32baf..994c404d 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_context_restore.S +++ b/ports/cortex_a35/ac6/src/tx_thread_context_restore.S @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -21,66 +21,50 @@ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" -*/ - -/* .set ENABLE_ARM_FP,1 */ - .text .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-A35/AC6 */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ -/* VOID _tx_thread_context_restore(VOID) -{ */ +// VOID _tx_thread_context_restore(VOID) +// { .global _tx_thread_context_restore .type _tx_thread_context_restore, @function _tx_thread_context_restore: @@ -97,19 +81,19 @@ _tx_thread_context_restore: #endif /* Determine if interrupts are nested. */ - /* if (--_tx_thread_system_state) - { */ + // if (--_tx_thread_system_state) + // { LDR x3, =_tx_thread_system_state // Pickup address of system state var LDR w2, [x3, #0] // Pickup system state SUB w2, w2, #1 // Decrement the counter - STR w2, [x3, #0] // Store the counter + STR w2, [x3, #0] // Store the counter CMP w2, #0 // Was this the first interrupt? BEQ __tx_thread_not_nested_restore // If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL @@ -138,13 +122,13 @@ _tx_thread_context_restore: LDP x29, x30, [sp], #16 // Recover x29, x30 ERET // Return to point of interrupt - /* } */ + // } __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) - || (_tx_thread_preempt_disable)) - { */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr LDR x0, [x1, #0] // Pickup actual current thread pointer @@ -166,7 +150,7 @@ __tx_thread_no_preempt_restore: /* Restore interrupted thread or ISR. */ /* Pickup the saved stack pointer. */ - /* sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; LDR x4, [x0, #8] // Switch to thread stack pointer MOV sp, x4 // @@ -199,9 +183,9 @@ __tx_thread_no_preempt_restore: LDP x29, x30, [sp], #16 // Recover x29, x30 ERET // Return to point of interrupt - /* } - else - { */ + // } + // else + // { __tx_thread_preempt_restore: LDR x4, [x0, #8] // Switch to thread stack pointer @@ -249,22 +233,22 @@ _skip_fp_save: /* Save the remaining time-slice and disable it. */ - /* if (_tx_timer_time_slice) - { */ + // if (_tx_timer_time_slice) + // { LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address LDR w2, [x3, #0] // Pickup time-slice CMP w2, #0 // Is it active? BEQ __tx_thread_dont_save_ts // No, don't save it - /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; - _tx_timer_time_slice = 0; */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; STR w2, [x0, #36] // Save thread's time-slice MOV w2, #0 // Clear value STR w2, [x3, #0] // Disable global time-slice flag - /* } */ + // } __tx_thread_dont_save_ts: @@ -275,9 +259,9 @@ __tx_thread_dont_save_ts: STR x0, [x1, #0] // Clear current thread pointer /* Return to the scheduler. */ - /* _tx_thread_schedule(); */ + // _tx_thread_schedule(); - /* } */ + // } __tx_thread_idle_system_restore: @@ -300,6 +284,4 @@ __tx_thread_idle_system_restore: #endif #endif ERET // Return to scheduler -/* } */ - - +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_context_save.S b/ports/cortex_a35/ac6/src/tx_thread_context_save.S index f36a8c7a..859a1e44 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_context_save.S +++ b/ports/cortex_a35/ac6/src/tx_thread_context_save.S @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -21,73 +21,60 @@ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -*/ - .text .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-A35/AC6 */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ -/* VOID _tx_thread_context_save(VOID) -{ */ +// VOID _tx_thread_context_save(VOID) +// { .global _tx_thread_context_save .type _tx_thread_context_save, @function _tx_thread_context_save: /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked - out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, and all other registers are intact. */ /* Check for a nested interrupt condition. */ - /* if (_tx_thread_system_state++) - { */ + // if (_tx_thread_system_state++) + // { STP x0, x1, [sp, #-16]! // Save x0, x1 STP x2, x3, [sp, #-16]! // Save x2, x3 @@ -140,18 +127,18 @@ _tx_thread_context_save: RET // Return to ISR __tx_thread_not_nested_save: - /* } */ + // } /* Otherwise, not nested, check to see if a thread was running. */ - /* else if (_tx_thread_current_ptr) - { */ + // else if (_tx_thread_current_ptr) + // { ADD w2, w2, #1 // Increment the interrupt counter STR w2, [x3, #0] // Store it back in the variable LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr LDR x0, [x1, #0] // Pickup current thread pointer CMP x0, #0 // Is it NULL? - BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in // scheduling loop - nothing needs saving! /* Save minimal context of interrupted thread. */ @@ -179,19 +166,19 @@ __tx_thread_not_nested_save: STP x4, x5, [sp, #-16]! // Save SPSR, ELR /* Save the current stack pointer in the thread's control block. */ - /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; MOV x4, sp // STR x4, [x0, #8] // Save thread stack pointer /* Switch to the system stack. */ - /* sp = _tx_thread_system_stack_ptr; */ + // sp = _tx_thread_system_stack_ptr; - LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack LDR x4, [x3, #0] // Pickup system stack pointer MOV sp, x4 // Setup system stack pointer -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY /* Call the ISR enter function to indicate an ISR is executing. */ @@ -200,17 +187,17 @@ __tx_thread_not_nested_save: LDP x29, x30, [sp], #16 // Recover x29, x30 #endif - RET // Return to caller + RET // Return to caller - /* } - else - { */ + // } + // else + // { __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ #if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) @@ -223,9 +210,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, #48 // Recover saved registers - RET // Continue IRQ processing - - /* } -} */ - + RET // Continue IRQ processing + // } +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a35/ac6/src/tx_thread_fp_disable.c index 2b7a0aac..3e5d7e21 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_fp_disable.c +++ b/ports/cortex_a35/ac6/src/tx_thread_fp_disable.c @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -29,41 +29,43 @@ #include "tx_thread.h" -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fp_disable Cortex-A35/AC6 */ -/* 6.1 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function disables the FP for the currently executing thread. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_thread_fp_disable(VOID) @@ -82,14 +84,14 @@ ULONG system_state; /* Make sure it is not NULL. */ if (thread_ptr != TX_NULL) { - + /* Thread is running... make sure the call is from the thread context. */ if (system_state == 0) { - + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ thread_ptr -> tx_thread_fp_enable = TX_FALSE; } } -} +} diff --git a/ports/cortex_a35/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a35/ac6/src/tx_thread_fp_enable.c index 431d5598..4e69205c 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_fp_enable.c +++ b/ports/cortex_a35/ac6/src/tx_thread_fp_enable.c @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -28,42 +28,43 @@ #include "tx_api.h" #include "tx_thread.h" - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fp_enable Cortex-A35/AC6 */ -/* 6.1 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function enabled the FP for the currently executing thread. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_thread_fp_enable(VOID) @@ -82,14 +83,14 @@ ULONG system_state; /* Make sure it is not NULL. */ if (thread_ptr != TX_NULL) { - + /* Thread is running... make sure the call is from the thread context. */ if (system_state == 0) { - + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ thread_ptr -> tx_thread_fp_enable = TX_TRUE; } } -} +} diff --git a/ports/cortex_a35/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a35/ac6/src/tx_thread_interrupt_control.S index b177e05b..6a5a7741 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_interrupt_control.S +++ b/ports/cortex_a35/ac6/src/tx_thread_interrupt_control.S @@ -12,66 +12,59 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ /**************************************************************************/ /**************************************************************************/ -/*#define TX_SOURCE_CODE */ - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -*/ - - .text - .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-A35/AC6 */ -/* 6.1 */ + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -/* UINT _tx_thread_interrupt_control(UINT new_posture) -{ */ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { .global _tx_thread_interrupt_control .type _tx_thread_interrupt_control, @function _tx_thread_interrupt_control: @@ -85,5 +78,4 @@ _tx_thread_interrupt_control: MSR DAIF, x0 // Set new interrupt posture MOV x0, x1 // Setup return value RET // Return to caller -/* } */ - +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a35/ac6/src/tx_thread_interrupt_disable.S index 11846ef0..d0062ef8 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_interrupt_disable.S +++ b/ports/cortex_a35/ac6/src/tx_thread_interrupt_disable.S @@ -12,65 +12,58 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ /**************************************************************************/ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -*/ - - .text - .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-A35/AC6 */ -/* 6.1 */ + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -/* UINT _tx_thread_interrupt_disable(void) -{ */ +// UINT _tx_thread_interrupt_disable(void) +// { .global _tx_thread_interrupt_disable .type _tx_thread_interrupt_disable, @function _tx_thread_interrupt_disable: @@ -83,5 +76,4 @@ _tx_thread_interrupt_disable: MSR DAIFSet, 0x3 // Lockout interrupts RET // Return to caller -/* } */ - +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a35/ac6/src/tx_thread_interrupt_restore.S index 8c8bb1b7..1b6261ba 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_interrupt_restore.S +++ b/ports/cortex_a35/ac6/src/tx_thread_interrupt_restore.S @@ -12,66 +12,59 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ /**************************************************************************/ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -*/ - - .text - .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-A35/AC6 */ -/* 6.1 */ + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -/* UINT _tx_thread_interrupt_restore(UINT old_posture) -{ */ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { .global _tx_thread_interrupt_restore .type _tx_thread_interrupt_restore, @function _tx_thread_interrupt_restore: @@ -81,5 +74,4 @@ _tx_thread_interrupt_restore: MSR DAIF, x0 // Setup the old posture RET // Return to caller -/* } */ - +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_schedule.S b/ports/cortex_a35/ac6/src/tx_thread_schedule.S index fb1411fa..9a7a7262 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_schedule.S +++ b/ports/cortex_a35/ac6/src/tx_thread_schedule.S @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -21,67 +21,54 @@ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" -*/ - -/* .set ENABLE_ARM_FP,1 */ - .text .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-A35/AC6 */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -/* VOID _tx_thread_schedule(VOID) -{ */ +// VOID _tx_thread_schedule(VOID) +// { .global _tx_thread_schedule .type _tx_thread_schedule, @function _tx_thread_schedule: @@ -91,17 +78,17 @@ _tx_thread_schedule: MSR DAIFClr, 0x3 // Enable interrupts /* Wait for a thread to execute. */ - /* do - { */ - + // do + // { + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr #ifdef TX_ENABLE_WFI __tx_thread_schedule_loop: LDR x0, [x1, #0] // Pickup next thread to execute CMP x0, #0 // Is it NULL? - BNE _tx_thread_schedule_thread // - WFI // + BNE _tx_thread_schedule_thread // + WFI // B __tx_thread_schedule_loop // Keep looking for a thread _tx_thread_schedule_thread: #else @@ -111,22 +98,22 @@ __tx_thread_schedule_loop: BEQ __tx_thread_schedule_loop // If so, keep looking for a thread #endif - /* } - while(_tx_thread_execute_ptr == TX_NULL); */ - + // } + // while(_tx_thread_execute_ptr == TX_NULL); + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ MSR DAIFSet, 0x3 // Lockout interrupts /* Setup the current thread pointer. */ - /* _tx_thread_current_ptr = _tx_thread_execute_ptr; */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; - LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread STR x0, [x1, #0] // Setup current thread pointer /* Increment the run count for this thread. */ - /* _tx_thread_current_ptr -> tx_thread_run_count++; */ + // _tx_thread_current_ptr -> tx_thread_run_count++; LDR w2, [x0, #4] // Pickup run counter LDR w3, [x0, #36] // Pickup time-slice for this thread @@ -134,9 +121,9 @@ __tx_thread_schedule_loop: STR w2, [x0, #4] // Store the new run counter /* Setup time-slice, if present. */ - /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; - LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR x2, =_tx_timer_time_slice // Pickup address of time slice // variable LDR x4, [x0, #8] // Switch stack pointers MOV sp, x4 // @@ -152,7 +139,7 @@ __tx_thread_schedule_loop: #endif /* Switch to the thread's stack. */ - /* sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; /* Determine if an interrupt frame or a synchronous task suspension frame is present. */ @@ -237,7 +224,5 @@ _skip_solicited_fp_restore: LDP x19, x20, [sp], #16 // Recover x19, x20 LDP x29, x30, [sp], #16 // Recover x29, x30 MSR DAIF, x4 // Recover DAIF - RET // Return to caller -/* } */ - - + RET // Return to caller +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_stack_build.S b/ports/cortex_a35/ac6/src/tx_thread_stack_build.S index 06007fca..5b7e945a 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_stack_build.S +++ b/ports/cortex_a35/ac6/src/tx_thread_stack_build.S @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -21,69 +21,59 @@ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -*/ - - .text .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-A35/AC6 */ -/* 6.1 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ -/* thread_ptr Pointer to thread control blk */ -/* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -/* VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) -{ */ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { .global _tx_thread_stack_build .type _tx_thread_stack_build, @function _tx_thread_stack_build: - - /* Build a fake interrupt frame. The form of the fake interrupt stack - on the Cortex-A5x should look like the following after it is built: - + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + Stack Top: SSPR Initial SSPR ELR Point of interrupt x28 Initial value for x28 @@ -129,7 +119,7 @@ _tx_thread_stack_build: MOV x2, #0 // Build clear value MOV x3, #0 // - + STP x2, x3, [x4, #-16]! // Set backtrace to 0 STP x2, x3, [x4, #-16]! // Set initial x29, x30 STP x2, x3, [x4, #-16]! // Set initial x0, x1 @@ -160,11 +150,9 @@ _tx_thread_stack_build: STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR /* Setup stack pointer. */ - /* thread_ptr -> tx_thread_stack_ptr = x2; */ + // thread_ptr -> tx_thread_stack_ptr = x2; STR x4, [x0, #8] // Save stack pointer in thread's RET // Return to caller -/* } */ - - +// } diff --git a/ports/cortex_a35/ac6/src/tx_thread_system_return.S b/ports/cortex_a35/ac6/src/tx_thread_system_return.S index 5e338bb1..7d42b63d 100644 --- a/ports/cortex_a35/ac6/src/tx_thread_system_return.S +++ b/ports/cortex_a35/ac6/src/tx_thread_system_return.S @@ -12,80 +12,65 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ /**************************************************************************/ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" -*/ - -/* .set ENABLE_ARM_FP,1 */ .text .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-A35/AC6 */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ -/* VOID _tx_thread_system_return(VOID) -{ */ +// VOID _tx_thread_system_return(VOID) +// { .global _tx_thread_system_return .type _tx_thread_system_return, @function _tx_thread_system_return: -; -; /* Save minimal context on the stack. */ -; + + /* Save minimal context on the stack. */ + MRS x0, DAIF // Pickup DAIF MSR DAIFSet, 0x3 // Lockout interrupts STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) @@ -129,8 +114,8 @@ _skip_fp_save: LDR w1, [x2, #0] // Pickup current time slice /* Save current stack and switch to system stack. */ - /* _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; */ - /* sp = _tx_thread_system_stack_ptr; */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; MOV x4, sp // STR x4, [x6, #8] // Save thread stack pointer @@ -139,30 +124,28 @@ _skip_fp_save: MOV sp, x4 // Setup system stack pointer /* Determine if the time-slice is active. */ - /* if (_tx_timer_time_slice) - { */ + // if (_tx_timer_time_slice) + // { MOV x4, #0 // Build clear value CMP w1, #0 // Is a time-slice active? BEQ __tx_thread_dont_save_ts // No, don't save the time-slice /* Save the current remaining time-slice. */ - /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; - _tx_timer_time_slice = 0; */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; STR w4, [x2, #0] // Clear time-slice STR w1, [x6, #36] // Store current time-slice - /* } */ + // } __tx_thread_dont_save_ts: /* Clear the current thread pointer. */ - /* _tx_thread_current_ptr = TX_NULL; */ + // _tx_thread_current_ptr = TX_NULL; STR x4, [x5, #0] // Clear current thread pointer B _tx_thread_schedule // Jump to scheduler! -/* } */ - - +// } diff --git a/ports/cortex_a35/ac6/src/tx_timer_interrupt.S b/ports/cortex_a35/ac6/src/tx_timer_interrupt.S index a81edb23..5810b5c2 100644 --- a/ports/cortex_a35/ac6/src/tx_timer_interrupt.S +++ b/ports/cortex_a35/ac6/src/tx_timer_interrupt.S @@ -12,71 +12,61 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ /**************************************************************************/ /**************************************************************************/ -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* -#include "tx_api.h" -#include "tx_timer.h" -#include "tx_thread.h" -*/ .text .align 3 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-A35/AC6 */ -/* 6.1 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Timer expiration processing */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ /* */ /**************************************************************************/ -/* VOID _tx_timer_interrupt(VOID) -{ */ +// VOID _tx_timer_interrupt(VOID) +// { .global _tx_timer_interrupt .type _tx_timer_interrupt, @function _tx_timer_interrupt: @@ -86,7 +76,7 @@ _tx_timer_interrupt: for use. */ /* Increment the system clock. */ - /* _tx_timer_system_clock++; */ + // _tx_timer_system_clock++; LDR x1, =_tx_timer_system_clock // Pickup address of system clock LDR w0, [x1, #0] // Pickup system clock @@ -97,7 +87,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice LDR w2, [x3, #0] // Pickup time-slice CMP w2, #0 // Is it non-active? BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing @@ -126,8 +116,8 @@ _tx_timer_interrupt: __tx_timer_no_time_slice: /* Test for timer expiration. */ - /* if (*_tx_timer_current_ptr) - { */ + // if (*_tx_timer_current_ptr) + // { LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr LDR x0, [x1, #0] // Pickup current timer @@ -136,25 +126,25 @@ __tx_timer_no_time_slice: BEQ __tx_timer_no_timer // No, just increment the timer /* Set expiration flag. */ - /* _tx_timer_expired = TX_TRUE; */ + // _tx_timer_expired = TX_TRUE; LDR x3, =_tx_timer_expired // Pickup expiration flag address MOV w2, #1 // Build expired value STR w2, [x3, #0] // Set expired flag B __tx_timer_done // Finished timer processing - /* } - else - { */ + // } + // else + // { __tx_timer_no_timer: /* No timer expired, increment the timer pointer. */ - /* _tx_timer_current_ptr++; */ + // _tx_timer_current_ptr++; ADD x0, x0, #8 // Move to next timer /* Check for wrap-around. */ - /* if (_tx_timer_current_ptr == _tx_timer_list_end) */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) LDR x3, =_tx_timer_list_end // Pickup addr of timer list end LDR x2, [x3, #0] // Pickup list end @@ -162,7 +152,7 @@ __tx_timer_no_timer: BNE __tx_timer_skip_wrap // No, skip wrap-around logic /* Wrap to beginning of list. */ - /* _tx_timer_current_ptr = _tx_timer_list_start; */ + // _tx_timer_current_ptr = _tx_timer_list_start; LDR x3, =_tx_timer_list_start // Pickup addr of timer list start LDR x0, [x3, #0] // Set current pointer to list start @@ -170,14 +160,14 @@ __tx_timer_no_timer: __tx_timer_skip_wrap: STR x0, [x1, #0] // Store new current timer pointer - /* } */ + // } __tx_timer_done: /* See if anything has expired. */ - /* if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) - { */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag LDR w2, [x3, #0] // Pickup time-slice expired flag @@ -194,8 +184,8 @@ __tx_something_expired: STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) /* Did a timer expire? */ - /* if (_tx_timer_expired) - { */ + // if (_tx_timer_expired) + // { LDR x1, =_tx_timer_expired // Pickup addr of expired flag LDR w0, [x1, #0] // Pickup timer expired flag @@ -203,38 +193,36 @@ __tx_something_expired: BEQ __tx_timer_dont_activate // If not set, skip timer activation /* Process timer expiration. */ - /* _tx_timer_expiration_process(); */ + // _tx_timer_expiration_process(); BL _tx_timer_expiration_process // Call the timer expiration handling routine - /* } */ + // } __tx_timer_dont_activate: /* Did time slice expire? */ - /* if (_tx_timer_expired_time_slice) - { */ + // if (_tx_timer_expired_time_slice) + // { - LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired LDR w2, [x3, #0] // Pickup the actual flag CMP w2, #0 // See if the flag is set BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing /* Time slice interrupted thread. */ - /* _tx_thread_time_slice(); */ + // _tx_thread_time_slice(); BL _tx_thread_time_slice // Call time-slice processing - /* } */ + // }/ __tx_timer_not_ts_expiration: LDP x29, x30, [sp], #16 // Recover x29, x30 - /* } */ + // } __tx_timer_nothing_expired: RET // Return to caller -/* } */ - - +// } diff --git a/ports/cortex_a35/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a35/gnu/example_build/sample_threadx/.cproject index 8a0cf9ac..1c32cb32 100644 --- a/ports/cortex_a35/gnu/example_build/sample_threadx/.cproject +++ b/ports/cortex_a35/gnu/example_build/sample_threadx/.cproject @@ -97,7 +97,7 @@

Q>dP6leU^u3WJIStz4({jFOv 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 z%0D}GII883;Y-UubND6ta(3!d3_tv544*yxGy3wJ;ZV6S!BsHgdc;7m_va0-T2rGv zXAQ425p!{EB0fF*QqLbJ$~vQ{Zi(lYkpJ*Y!BSlL7oZQ>Q1mAvM*I=-v_F>>c~sb@ z-HA3+L`1`ANzlJn=&+nStfCs%?5|#DY>MV=ii$slPDU(vgg0#Vi*p?Bt$y)3u<`FD z^YDg7znB(}rPrT3Y~@9$?Y(b_xNUywUFK|(jCOg$t0oxVPWX;Az9#sPQKfv#8(zI+ zk@EUy4@XNyjDJq*6t+ft7CYQu^HQj;VeyKW`UeKX|6}i4z^o|Fw7XBAVGhg)qc9N_ zH(^~ml%DmHz06>jDg4u3@+-_<#izUp*z(#){>lk$sELI5 zxL(#jxUtup&}~CvH_lC773SbCPp%JhN5{bhcu7nf+?bwFj|^0pJfWcz7gwd^gbEl3 zmdn2;xrE94Sh(1Y<0hcL{8akd@(Jj&ub3`7c0w&GDuTZvxt1-b!i2WgjxgGo32n`2 zVNFyp0sY)WTQdPndH-k9F;?CjUv+bQ0_0a^&F4oZcB8H2xOgou!8;p5+Iz zO_-PzU&6TtF5!Ep$=w&aaHzfVOO~H3ymHw@;cgw zMc*mGg{3s?dqF8I3YU~EOk?5K8R^uFDaWQ~1;wRzl^qKqb>`Gm#pT=qG{|2RY%HCY z4lY9c(z4XTR9WJ~XQqO$Ue=hJo@#2oYwt+u^AjJ&4x)|0^hLpv(jZlOCKInH4W^dP zn!RM=!I!8Vmz&EaqzWxY4GoV*Z9J1(}R-I9f^}3O*5oqdTjb=oGx)r za|tXGr+<_xS)5JB znENAIZ(fLI#$KxdEIl7t8niD;rFSG=t8F&f2j98$ktL`|u&EJ$y?)|po}(g_hW+QJ z>E}-^nL52BolY;FQJ*d;NiCedEmd+R%9AQwdoz7#IZV7&SY2tw&l{7VPeQaUL8J|g|6DK83Y6|W|=odfwM(_)7%eG&mnrHs= z)ZkD4mb>o2RVzV@1iwQ!{QjknBtD7a;hMqp^`hV_{tqj7`jXTYOJ@cbFDeU?uw>oX z6nq7BOa-G$Pd`6+hB#FaSq21k88M@cz7O(M|Kq{ zouHf3k+p6@<^C2w`wfahOKVG88tahc@lmzDp&bj98rzNOUc0!ep{=2-siD2@q666c zHd^YJwHPXUL05NYLtRx}O?SgUd*fEM);7v=t=5J{*o19r#kL%00gsY)GX)O54Qmn3y4ej++$8$#*T|>u|%^lbi2J5pLv)1-i)pc~NYiW>t z7%t2IWC^M<2W{4PwRBh2ZK}ZvFs@`Xi_owgl3n#?7x8s8Hg@CfTHjFDx!#jvz=yTs zS~oP1T{wnn!e*Y8bj_Jry4J`GRMVO z{0>_5Uf-6v=P+LOW$@lu*IbKLZLJ-+95lvEi+y`$|<$4Q-ur1;Q13C*ywRp9hUO7 z$wEFcDJTW6x~QS1%UcIq%nc0YdOWlpFP$v(GiJ24$zemVg{%J}^XlyPW!^@#8(IxJ z2AFbqy5co52Ocd@tnKLP>Og5KmY%w}YT0S0u2^>V!j-30E$8*EsrOnq4y>!`>bfYr z|LPlB(KKN*m@X&n6m3*(7G2iYAXQbjEDWqVePP9ElnnAF)WwEvwN5XH)+}Z(K6FB2>WwysTz5iznHZV)DGIv9qJw?pkd-F*;a# zhuModTuF(|OS!0#*8s~R8*94PS2fnOv|^g z1lU2Z$1)mg1V3Z(vaN6(u5nWb8+=3ihVF)XSq++HeZ3K9$6c`=rAFp*v)0zK!&SA; zo>?On?hkAzhAg@3gs|svC!u?*m3(xex^=u2+vReaqM1f%>8@uLWtHgqmbM0CU_W!A zE7zG~Gjnb-ZyJ()<8O0o?+!j1O0m$htaPSV+SSlnTHjE+0qxXP)7Id6bZ)3^hgY}j zSA&koIvQSXlUs!eVmb#W4ZP zmAzhcB?c;AjRp*1nU)}~o8P8WOnTcYP@=_URvRzm& zww;?_^>O$hXfr{Wa|kJNuK&BSu9Vl5j^pSUO2umtIkz;d&j_sT#5zOU=`{X}j?C}} z!|P@8+vCU#--!{paVC5WJE%Bc^54XZp)$*Jiz74rbE+ZEZpA=)x=G3ZKzgbxXVxz8 z!i*(SAZK^ukCy&veRy4qtN^5ZY?gd{mV8>4yevzu%92~NWSJzXAJ4ZvEBupL@;zBH zKls@>(jmBBk7kAUX35{mlAq6#U(S+W%aVVUCI2Z)&T+aF>vK?+%r&vrF`QZ;>nI0b#_2dEC#u=EoHaWVFxhlSr4J%f>3Ok&OwwhwOwyUR4R0DzzE-lU~C-;(_1c% z1Y{@nGc*;%CX8TgI&M@S3Jk+oODCUI_0w^Jc^pY2iaWi`9t6(Q$w&(mbarfAN^K2ob)5)rZb{3K&hskJhT||HWA|9Mvqs#E^>VLcX-%Fh6dA-C*xJQYI_aYJbkHs@$ zT#d|0MEKuJM0p=lxtECgzDYzoB=DeQ`O6i#*oEo#5RbyM z01teYlWR_i^A*<<5$^^f+JQECdHx59Xs?HfIPX(L#QQN3=Np4M^89Hc%1N8NOkaR! zCF^?waNIP=`M?r-ijUIVxn3ip-*T)OL*K<^f^|Q%H!)_$gv7CwF@{A^1yM9XWF>f_;ylHLisvde zD{fP~M)5Yqdlmml@kzz+D88WBr}(;J!Z+s^TLvg!vC3-{YZb+o0m3g;S!@|VzD?zO z6~&eT{GU=;Y#BhNGMx1nTLwU}WdO`W+t6QZ834tW0g&o{`invySf}y@ie#HG{PT)p z%K)<2G64QW{qynslKd6LmI1=WmH|*~836g5WIVBD0OWI$@26k7&Bv1I@hTLwU} zWdPi&>BW`-ct8-%upW zm**eJ_X}{m;(3Z~ivOtinBt!lr{TfHbXO=|M?`sUQu$LV->LFvRlZ;4FRA=BmH$QM zXB7WK{ePhH3o7qYIlz00=bxzfQ9dtlemq{i82;q>33;x{gJpjALXEHd|UiIx*Yj$1V zH1NdJkg;a>m0{pnTb_SD`0E7-C%=8{bJxApS*|t^aDopw)d#?-9*E{VE|WPuK9lvl zx6Szu-l*m}qbp)yBFCQhBS?M;LSkRVUqoyE4Z`Dw*35SyXwByWKx;l5Zwi>6pNMcn zYlhAFnGga)YyKp18NlYeo&gq;amvJquOU$aHs|kQ;*i$7jM5~Q4m3GWtjHRh^YP)# z+0vRv@_i#V=VN5xsm=Ls;gB}x<*n$_nqwiY`8#~kg|y}>W}{Mm-drba=r-qRi=Q^< z<&_`Onx~<)Vj-=$2xX6jwB{!1M*_WGEKo>mzMtpFrZtm}9*bzrUqhV!ZO%_N9f`E&*nLJw|Ux^xf zv^h@#F>KD8x_gD4Yzb>sK3;eTL#>!7EajE3Vt(OlMviFBFS0QroAVnPIifXhWaNm} zj2Am@F+J~`!n+wcvN=DFkt3V)>litrHLqc4L~EYUYZB3#|Cf;?TJs)OA)+Y;d|Sh=hA|hLu-Bj(ZuGwJOCnEb1vgWv}T&D z+?UPyKckhz=KOEjToJALlS~)Unr~*I2DUjLli84CbABRoi)hUIOKYah zdETqm=KNw7@*SczA8{52IsR7E66WQP!%68llz@48utD<}JZ$;R@fA16$M2wb9^2ku zHvV5xC>72g{~!|jd8?V#S>w5CK!vsAgovAWo`m4FfJBM7OQbxqn=KR?sGm;BJCy(L zTUMle@0N+lTe47}Dk)u@*bqyQmbDiL3!fQ7$E!JiWWj=wldt!$h+Q6R!^Jubez;6< z-MBPluvW=8ZSi7a6R7N^Z?7r zF!T_IITF%}s+3101fQB#!zzdf362qZU|$wck!0{h zXuqAhkTLk-RTc6?IESiE)8NqCgzu@d9S?iB1YvG|v>VU#%#UdE ztHrTRgG28@d>>`qth4QW9z5Lq-b8-3o$)C+%&!~AZhkO!BswUYpAGBC$}f&_fGr=A zg!x^Wl^@r*B;jYB&2K9_-15<`y)7S9i(!72e`M=89@E6fZ6y(p`SEv`B0$`KLEksW z3P5|_`*Dmu)oFu9<6AA%e4(@PkUZ>D3@k&uN5WW9evEI1Lnd%7h(c+2lJ~^=u&6}V z)%Il^de@@Al<+{EJ+GB5y8iD5Ux?Q-a)!TC8G5*-VSU(>BKLOKp+_@|?c?0+6yhUbf^A@-HG5hBG)q^m6>h;o`DRF`P{F1JHf3-UI4o(dR@j3!KDwz_g<1 zWg*W~d7+}{W#Qkf@;1e56mL_!SMi?|pH!47H`2eLa-ZVsilUcAxaehp^4S6?dRd_8 zWq}{qm^at!0~_8lSp`u8WeVEITtf zJ)NF)!Hkl!Dc&g@rU~H1h=Z;(XuN5fA3z-Qv*)6RJ*MKr9@Bpo{~Tw-PKTd8 zW-#ON-D8~%TLuq%%rMIRjdwl*e|NldHDveukIf@{yz@E4<41PuY&;@vhtP4`;2uaA z*2-vLpz+QvCH#dYIeL0@Kd6%F3#>=JYp3#2#8)Llmcl*$$uj2S+KmDsdzkL2nVrkP`J{s18zug3j zb-0Qn&H$pV@H|{jYkn`#lCX&jOL9p2C&WdKUX5X=2V;#77DB7P@p1`vH9*<>rlx!D z_Ig{tjJOZscTXKGvjecVO0=ik4@aNlljJ;sMxtc z;`Bgn_j1`+yW>6F z86XNjB`Z8B3kW{dXDxUK1t=fNHV)Ps$?(`b{5lxB{L9bB;o5k~TcLcY#PzCu34Qih zKm8o8$5StlOBzdqF@&M~C*;ENG48nga|>!vZ(M_dvrzY^UWSFo!)YPWOFz{&0%a{m z{q61!o)?t?mNR$%1np3L!w%H@Q?N{bLtjz)O7xXww2u0M2TT6T%Wv*`0&$9au6$`Z ztbxJ;U}%ZaD@QwdJwB{ejxcez_Eq+5?c0-K70_CN-t({Q<$JaDX5@i(j#Z}1a__V;}FC3?V)L4S%@8-@(qj_nqgYn0V)urP1i zZJ4)0+HF{6gK4+Q6??Ut?`5{z0^3HgJ{yDeN4I^RM}7SVw@CZYiZ-uf4zFLiX`k2J z_IWUBAGSqMjk;I%?3#xD=k@gL+M)f%_A}_^85^I zbka@8O4ovG#eQbfuy6Aj#p@=|DD-jrjN)~Rcbe-}jQ$)wpO)?NdV6-^jfKAS9O`?v zJZtc7gMLC{~^A3ll-Qk;ie6(4B_;EZJ@jSzM3mAWuSfBP`jry*WNAJYL zZ$#QdILhEBKZbHd;h#ac?>+r8Ez`2Bzmfa#MSZqDje9vNJJQLsmDXXwx)AHA%nSFm zsfW~&Rz9CL&sCJkOFu2vIHx)Hwcm@j=%qz=SW9)T1$P1R#$NZcb^q@#KmQfl$qnQE zbqo9wh@X#myw=#Y=T+XPj@w*v>bRf3H2d(Ajo+S=_nczhOAB`Xr0=xeB`^8aJ-f=w z@93+j#O^StLq2v0ISSAJ3S5ls_%-9V`mgYF&tFh}-7AU8>-wgk9mufbJ7Lc$mQ9Ce zetA#em7~Az#EuDw7e_y3dtg@zS}QjF8SjSdKAnU9;&1L5&3-M~zSk8WqK8R)kuHBu(J`T&Ze)@@*`Hu9{d(a=w-i@7*4rh3N63;&NiAwg#dD8Fw z$|qh9dhy&u7~jvu>FbbYwf1Xs4`FBl5^d!!)2E+6x8kk{J>U1S%LDuO1F1vsZhhiq z>CffJdnc}E@>=A7UfAEIKbJr8GR7dgev9(3PxBs)$ynhD^yfW&Ovmyfo%Cm>5Bso$ zoriKi;q>7r`fg`mL%Z;Lu+46jz6Yz`2m1xX4#EFXBS+^2_#PN5hSkx$ypu4@3~-n* zhw%@r#d?PuTeL7P=30b3*gwQC#}yqKt8oBpv3a-vhhRWHaUjm5Ycn>-HA7pZ>`7Se z<)pbdG73xaD78DQp+_t8QpIEP{1q5SO&;%;jW*|>lH-+Of_p51>8H-rgK2J+ljs{I znw8k@E!9GZkkTTEkk$f-P!<*LRQq=hS`c0P%dliDc@5&EZgK8^e>nKP015E57^Uo4qgY3S6>|3x08 zc77Y}6AP)GQ&HN8+W7~Dh1AaJcq~TL&K)R!MD3*Qu87+CYnC>oc2-jgsh$7JGh|Xb z`2kg^o#<;`MD6@C(?u3*&tk%e+Ia@sU=V8Oy(pDXJEdhqYUdryIHY#|BQpxAo$@hv zzo?z(BR5hzf6c;&)Xw{uYc{p>|9Ikv+PMkheo#BhnPo`ryqC=}0JZZT7A2&1%6I99 zo~2*WxV&HTN`=(Ujg&%a=j$wANbTf}7klKhJWtWIk=QBRi~S2}Rlacxk7nK>wR0V%?>-@C0JU=^3pzNp zlPe+LThvaj)f8&y!K`{j?feYuZpoYR!Y?w^iitv@@mevzkj5VzYA4sjJJilgm^Pwz zeuj}FYUhJ&$cWncI71_9=W2#V)XwEBRYdKSg-;Q+la@gpYG*FRh}uaroDQ|Kk)aW_ zlXoV_@|yuc(bKWQHLQa}?ffPy;ZQq^n43fG3@GnS?Yw~1aj2anPYbnE9sm)wa~e+{ zQ9CP{Y#$bDsRb2kr;s8eYNxDli>RGKeccDO^LU=sp>_(%F`{|dcC>>O(6R)#y&PE2ewsGXNk-WRp=Iz-qXYUiyia769=Z=U}^sGYo5Ew%H1 zSm}3&+8Jv^31Or5Nt7EFYjbcaNV`yLf7lV<#W%jgalQOcArfY9_y^-t%(31y8ShKK zavUxD_&1Rg>aQ4o9wXdDqM#aQ9X|uv`ZvkL`Nlcp>zM6LA7hly4^VSNXJ?xchq#8BrB5tb+Sz>iuI z6Xkm*3I?!^7&ZFqdH$aZkCiogo<%&@l)X>N4jL;=h>n*W`y}nE@p9%qNxyo$l-39z z8UJyn?~?*9Kk`3Wz}G}r!-Bsqhn1#Azo38`9oftx|59>3OD%>@;${>+hPIwxirHsbLu)o5a^c;(~h{aiHipQee%06)J(b6H7`qCrTn8GeK zg*7c{3X2G=_Nt?$%PcjgU0o!7VyQ2k;v+{(r!X;t8(;KHaaWnR=NyCdUa{2mLdh(4 zV{x%mcB6_(Q;SWu7Odb!CiPnA>>tIb9v29IUpJLt1vsj=m3Y)tqOxcrBRy(Tt~6D8 z)Kp2W4$ovKLUhJkUL@VzNWqOyd8Sg2no6B#Dy4B(OlBeYErjQt@-&2Fzt3WwHRZo~ zjE#mo|GX)0^7t=2UVZF{gBW}aHI7qh6&AIgO0%)C8Q#R#y&F(*81l zO-b9YX0kLI`W)t(@_AA@OiPcFL$7Qm+C5@$FF@ap8P3A{m%(inMQ?$?eXH+Rrh`q1 z7@%(_f(v9M+$a0gM&&_)22_IeQe2$bj3EBRZVXzS)8gkV|uW# zG5Dr;=OS3^EbWCE+K1D>4~8d_G&@?dc>5h^E=~n4jX~#~!SF|diy9xBR+cIWroJK3 zuc$!$#NP!Q7s0Blja<_h48Jpsd358JvQL*yT|E8r$8gT6uqYcGl6c@huSgVKR)$2k zr<$(%SgJmiDtj!|wDec0&1u*?U6{IS-5sgvOM*u(+Y%Hc=H4C5O{`y(DrpKv#wz@l zVAGbx!F3;fV8ItG!qQ8P+P{uqe7|0dTgaD6E<*Irf z(Xr+xHEu+8_L~L88{j<-XIYLPOJNRwtxD>Jw@Vt7pS<4|QH8Hgvyb~QgW6pKOZd^M12j%H6aGNWpl)obU*9~K z6;w3~Yq2hnB6&?dvJO!ECt~yckru#5zI*>Kfx3n}NdIf}Q1&r-exqHoO z4`&^VLzn-nSwMZ8EuYdBdrP;NoIM~Dr}p_*^%hN?Mn+EGf`QWR^)+3xg2^?tOf#jO zT{NHDpx78bssGmPmcFG01@C~_QW$8G0la;Ak?Hh%3}CIZhh%}_0#gBOp7LROT=5HPIBKslU-s$(n7Eeo%#Zol{AzJ*(=diDfhAOO^bv7(}b-}Yv`8dwA<+~EcHVyMQ5BVi|pw5PE zh3sC^=aF9l{8+yJRu^b;?^5JfZUvyt?;btob*6h9@#f-sSjTw${iCAw|Kr5|S$Pj5 z0{T>^jW>PL&{iQ-BHq(B5zv;87<2p|JlgX%!-HRGc~7wYSbw&O?aMgy(o-F$Z0AP|w=YtBm9VeaVq^JJ zZEJ#XEQT+w8BSQ<+}lWJIEH|EdRqI(h7eWGu-s0oT=Sid;ZWKUrR8)pWHaVFTRWP{ zuvdi4yyl|JwD`9g#iixOdx)UwK1DndJU1f3`DJ)q5|-Y8v5ELZgLa@lw*aI+`yu_~ zxJ{UTF0lyLgIJ7yNW}fjmD!Ygh=}(h5&3kYAM*UYz!=W)l;YEh&niBr_`Kqe6<<`` zsra(uD~e)~4f*a-`Ax;&E7I~N&r9+wF|L?U%vUT>9HTf^ahzgOu~>1k;xxsy;!MRk zigOjq73V81P}~fp1`S5a2}!2d~=W#td#7gX+3d|fete#Lx5!wM``Syui)mX$w1S@{FJRKu@T zyiM_5#jh!{-!fmZ$OsgRj6ku-2o#Hqz&u=I#uM{Qz>`!Ki;R%PA|tR){Y75|`EHdz zuP7E7;V%{$fntLMC>9xkhx5Jwih&fMSY!l>MMhw?hKof;$d{-r78xNwsj{s6fh-mo zfnt#nC>9xkhvGisc|=vUDP%JV6=V`cDWQ1I; zvRGt9xkVv!Lj_bE^;G6KaSBTy_d z0_%An0>vUDaI4BA4ep5Yj{^L*nJk5xQDaSaja z&QrXei1YMn`2Q%rs`wj4?B_1|en?R)LgGBLRIX4g;`0pokKuC@e~2P7IQ$~dQF*P( z)hf&L8u7QQ{D6i(sIt5_@Mn?ZA)cq9kt2n0dB~tEzvdh_Yql&88N$l12RIGuZjED=cJ|qF&cmcV?;BJ!t8(02m0{qqjwHC>BB(ch#U5x9OgD+Zb_?Gdcu{7yb z8_p`zSPPTyWZrvFo}ja5*BF#1hO#s#%~}c{WkfxM3rfy;Zg0=7k^-zvDR_2FMZu0f zzxs5n*}bvPTE~o|zD?lfdcF9e4z#36i<#t-N_wy7V2qf8G35F>mNTUp^)HRG#90oU zn{C6iwq4>#mqt4Hy^4JL=cOtcHE_wO@rbXQqyC^bqSGAe`=j&nPRKhEcT4U;Ahq(4 zAqgKLJQzOFTR1v};1Q#pxajpZ?i}3E`J+S|a1f)A7;7>diAV{PAtQ}qBxYHl(Q-_* zgA2W5a5EiZn4^p^ItS;(t#Y&gl#OyOl$hCk$TGz;q6f-N^*n6KVGF2~&7Y&3xCi0lxb2OChd2vA{}zPAXyzrJ6C8$7K|FUNyyG(v zGoBOQiDPgzA3*@PnrAYEL!WqVXpOTP>4W4c2+4f|Ap=l0HC4O}899{ACm;snCn9mI z$jf_%(xl}g#l4`3qEI%2+cCTdj+%^+{CGeuv$4h*PvGQA+3dy1N!dJzp_!CTlYcyq z+eF8v8Yz+YWhAgte%>la8aicjCsL8J`6HHjy2;tg`+q#o3?s$!hB4=vMoQ%UJBv5V zNcnlMu<>UbsUS~mJRWDHF?qslnq#D~c}tn+@kSb#$34(vCm1Q|6-+@kwBII_&BIxi z`SfC&LU1XYNhXXan@f>sFv{kykdsh0rDa3P=Cv%)DpQ1b-WQlrNZAxya{EQuJO{an zHBROeQa1mAQj?wooRF(gnP3-B+%W@(2hs3l7sUQ4o0`s8<1|7dZvnFmDVv-|4n*1f z2e#Z7OuDgo^1=)$n^Reakg~azZ5>iJzssZb3lue<04nW!b z4bS(CNmr3~G0*pHBUR>2V97$trcg^m%4YmDCREBM6FAm5DWbhec!=*ov`oro6uBM| zMr-8raXf}8M#UH5|B#R4&;NH6z~n1O8g&E1Stmct3aS1c9*IZs$ICo?EB<0$;@?~u z59`SoS$qX063XV!ScBvEeSm$v@DjugIhygpc2`UkKI}W8`Gr?9?Ma%ppzv7c5n1D0 z$h4@Ggc9NZG96c^7Hq`Gqo4iYS{Sm^QM;$>%gEo2EvU zgxzgI(O? zYa^`0S534|JbuKlndw@V;ER$eGu#$tdf#4!zE5)2Lf_{W+6ZgCBTm7X&!572JSX%xON$E2d5#mjbG(Q)$)5CCinD2>UoG zaQ@mO$Dp$Q*F{+69kG*Hl7sGlMzUmfWFDC;?{o`zr{uQW) z!tlsEZZ<+?NTb(-BkDao-j6(J^mrSIj)Qjq4;m!{7Q~OGsVt1O)SF?mHe>s1(;rRW9egY7x5n8r-|y;lFKF`7`rS|4VV7mo%GQ%* zJrJQRiwQQ=U9h9=x2=uROiRiG?}9zh&ezI`tJ1Bn2P1x|0bRpi3+=U%vc?^*)$r>+ zI9}!{bKsj<_p^UBX^lL0Xh2$f_JQv}E!E*Z)AA$~JGN%a@dLJ?MYW+?(88Cs3=3Kb ze_uAVxJ&dPcC@06q>U}DNH+GgBKfbk(R9B%O;aan9bQi2ppwnt>a4|^XEprB;K%5E z{V?uPDr1Nx?p`M^dRTEPjv0TjcxfjM4n5|_If`}ma2`DDF~d$l#(cY4XT!*A;!w@H zhxi7?S5el3x&m8|Y|d61grsFQ6U4KJw_r z4?j1*$Bl*J{I0~YO~Wj1Mt(^i zsIy^PA-m-(Mt+DYmnFzU;X?$5s|H!#Nf;E<6zNo!~yP?dT@sN3Iat)jEdp!D}7mDflt& zO#B!>1zf9sl<99BnAldx>bw9}~wS9g51nTMmq&Z!V{d^v%HW;FVdAlsEF{23tI zm+}%t;m<&3yV0N5f_RPMZHg=}{e?dRd{SlM&p>`bSHppHuu2 z5$E};;VGIgS?)<0ljkCnEe9#hHp{C@v!+{z}C*^}kZ_HpS0s_rOL}SyjA5+mHCZ_>2FZ^0hJ$AS)Q}_vsj@1 z4eb}Ubak|cgp)z-k@C z!uFPy%W!f&J;c&1nu@1Ht45CR0P%>G;b;zi0S^$3U%KMCIdP7<<9sFfiD7*6`pJp- z&yn)yvdMk3qTTr9#F>XzhhNN-7tPzc4B4~SM7~+Vv>;DI$FVZU2^vl>@2!2T(2HM} zxnR%c&mEUq@O1_90j$cDsr~^^d^w*xVAIhubFEq5zxn8BTD!Li=_ssoLv4Fa8>~l{ z);H8{Xu`LGu6NbKboM-z&jGypLT8WJPw@x~-P5?i@u>Ai;m2OXc$B9qnp@J`1vU=^ zg?XoO?A|==>8lw*oei4@*&Z{9&mHzp>ugx|e3tz2YD8lB_$;#JTY_Vo28Z5X@S~d6 zIvd9Mr#)sE4UqIV&su{pw_V8lw(UZHd){^&Ge6dwZX1Lh=1~3_#@|JX;)%Z^G_E`a z)Pr&8w%~_0?i2xkA3?lvRsh;~7&pl00W}k8^hESLC-D=C?}a zdLk~CrpTZ%D zgug)40f>apB8sIBCi2$ep0HAW-kVIOB*UQ-2@gSzBoZFY)FF{@J{m0|5~dgy5(y7w z8A2lAM3x~W5(+;lBodOp5SufRl`R^Z=QCzVBow2FnMA@XaT*~KZe_ZVNVu5kBHNEL zk&lRkOIVsgY(M592O$zl%Z5b4Im|dD67t0t5eZqA*nSZSSD>0C5^^z}LnP!mvWbNM z#ZyH@!Y?4seh>+7W0oP2@SDtQ0NanV*zzHfP|W>>L_+zl5fTYM%a|dNP^e5Hk??U= z=m3a>cd?uykub(`4nQP4mNB21DE*`&kFWZONcb$%g+#(TC`CjVM8cPtJR%Za!^|ThA;*`AT@#~(6sL?oof(;*T*&x;<}ek6m|Arf-Jna9q{6p}o#CypfGv zE$hKzvW%6c)(w#MGKm#tiH(S7&It)T+?=dqT~%vMH%{_)?cm+iupZl-HIN*Ga#=FW zTieDvg9g>!un`0oUe~I&4Px5>Cu!-fMEE+^bz279 z0e_q3_n!kIWxWpwS^O@~w>`gQ(eb;q`zEmO2foZ14{v^Pa}_NxjP4lxn0~N$(-6m| z!J+UF&UZZQA;+8cnDIUbfp0nMY}g6#aJ;=AgTqjO!gI{Yub8X{%e4eQn+As-^>))8 z4|})>VQzk}9B$GxKYV%)^UMBzJq9o5G{UX3?Y9OVZhm`^UlM-IkN&oN{N`@UfJ1LS z^5b_&>ulIMc)0l$;6k!~%#WXUY<^pDY}4S-yAb&$d7#dQaUG4D-&o|w@6gQeApC58 zH{qCRY#l#={Jv@{sbLJiLyrOC7UKhXf8W__B4ky_A zdwXuy7jpOPjPG7C%DnQC3*^eVGFu>WfwY7xTaB)1C-Ra&>5)>hq=u&y4s}6Pa>kii zV@y6z2L1Mqz9Zk>fm0YC$mb97M8$cEyl?4$u41#|HpOccZ&SQiQFuT|_oT|-QG7wM zPf>V4h{q4OJdf~zfW;~c4+ygGfPlgS0tycZC_Esb@PL5#YC7QoL4HbQ`FsdjctAkm z0Re>v1QZ?+Pv1QZ?+Po7HAE6bio$-GImY5g?m#=dC89mlNYoh~Y0HGSGlkchO{u({ zUO6`JUH|_$Z?8*c*m!=y054^DxQn`DY^dXMoZ0_b}2^UqCOOBf{)Rwuq9 z!qJR9`EcAzp8p|M%U?PCM#7;YtQmeeg|Q;6@N(je zpA%;c>_&_7dVj*v+q7u2rmN}V1Dr!*)lln(`i6m0x3pu5+S;0`ZD~)HmZn-;YEw;h zb*Y-Jwvu)jX)kH0sclKghVm(!Q*__Tr!n~wP};IC&L5e)-oH9_Rj@sNWzH4DF3;VT z5L>5`zgL3Bz8$gb(?k-E!@miRi_E{?@CYTvS~*P1Ngt|eZD>D$FDC=6LX5n56YxV< zLw!;{oP^~IKk9TftcO8(DY%CNDM(#MdtJ?XiumxA@m)dEv#gRnBZ$@L&jfoINeU2| zAwMue8arXi)&d_ROs%@QI=ax3@BT!cJg~q0$@&CzN_1dvn08LSuX^>EV7o7-ZW%L! zhcPW!0KYN#F+JlY@S}=_vYnOi$?7_W(M`pV@dt~?IiXF1Ly!6Kxo({en+Fej%rK6n zd1qQ@4>|Y8P$6>phAYK-unlZI$j`B982Jv|%~U&BXAe1taP!-P{8%sMN3{9X;@GCa zp-0xkOdhDS?aaBqo8Rw|AIB@q53j{AKY80g4jnS!wY1Kjs{Y^ENBqK*Y_*H$2xYJ)|V5%KElUOGtUI@}+f^DLR$U=D?{kBED)Yl&mri6gk* zIQJL-uy^)ohIwg*oxS^)vOjtc&Jo#o^u6+zv0+f|<2KFE?Q`;B9ouvM$T15zg|Vhx#=}oC_g=>ln@S$1QW6Mw z(BWqypgXH$M%G0md7Z~Fy$7Xf754!&vjJ!3kvS&`rypl5S&=+7DRaBzWQ?X8YPuVy zBqyKRQQvSj^uBcsT^A)!S$1Y}dOAJpf*B=cQ=<8Z4E>{Q!+8ehJQ;#ij4y?^I_0pJ zsUHI)QW#dg|H+Ztwr8Ar>IuoosQ8rqs`n|pVh5y6e1A+*Py8IST&_))eaubnt{9mD82Zejev;-wMCroo}N6Tgdjpw5QPgKUo(L>&O{ zLhEeUN$_yT**&PobMWK)cNl)Q9?NiS)8J6P>GLUXoej&Lr*Ir=+l6TJtHrTR!#vWc zIO}GeZD)>4-12Qgeth?{e0;py{IcgM)yQum!mYFC%ATiecgn}-o6RqKp7Jr|m$aDy zZGKyE?3V8ar+oXGr`(17BAWvyKjwLtlQ#}U*TAu(b@se`S0Rf|>wj?0(B>&A#2d;y zg=<}@dmL<@!rdIF*g^np`5Z4fbp79rc?#P;a_-sLZtOXc+k5R!X6pWQjZtdmcZk@* zpdX(VoTs?&M)p_d)#uDC%&U-F|AFt+4klC=qt3*Sa|hmk`!n|Eb>LV!t$3Uw?{WH{ zrdXkPwqms+PtJH3DQ;Hey+Hq)74IbCz5N-*`_%tyieFcJPVv7LpCBR~@7*Df?LkW$ zs@`P!@nu6E+jH$2T*la*Hsv|Cr%ic|p*gnaxE_~cpW}LDH6ku!_wV<87-M@p=g`6I z7~hQP_vo13wMXx#w}QEg`9u1;K8)kvL*w<$(m0;C6zD&Ozy8PTpF;?S<_aHU}D&{2v#l!H;F!G^}iuByOTy}L105{vi8!*!oWn^AQbScF~2 zXfI)oXK-u6WsKEP3i0f3q`p}~%-`EHQkNktPx&`BWH(o8{|D?Js1lZ)Sz5DxeOF8E zhV@|g4%ziLM&InDwqwWaG5T6OmF-v*kA!fncNC5(V>~79Aw>%eqnnB!(+?Jp?_Zk+ zhw^D)C}VVf_aDj_y&m=8yOr-zjuGsbHG7PH1a9b;5pJC=Q}!5rrZWcK*BG4@XWgu` z?VLSEpX-c)InuJ_%O0b1%+G6Soh@JX7`?(NABG`ee%WJmZnu!MnE`EnTX8(x@n7zg zZ(n2d4&*n}W}^8~yi1R5mUp9qLm8w07vddh6FGVCBNR>*x<4S^P{!!AS2L6`I_<@< zk6Y(d$$AO8{_n;Zo!2sQ?%CNM>^YHpJI3gvG`r2f{&WdujBXz1_S-+-M%m+Y-UIK) z@%g`^Ui&>hZ)@mkY8cwL_ZhQh&m8KvckYP@moYxyv()Tsw)Fbfi-F|mR{9T91n0)2#LqYF?C5+vW4<{FjaX!rbmdc&@)xCw} zEy0o;zUzN7+%rrpY%IdaKGB~>%MN{^bT;OPQ|Ti#)@&dY4i2gw6Pgxt1Pk(MJ`w+&@jQ{aTaMH!2$>wI;SA1=FJwl!@peEaN9qKo z7{|XteQUtDr`FrhSPNF*y3m!wju^y5Ln zq8EbBEtJwT%7XSE;xokFdWd2)p>@2}^G6oXA36DY|C-p+Io4@Nnz#7zdUJ9Bh8X73s&@OiU8VI(vTf*g>XV%x_=wBkm96 z&I4>aXU~tGLVg9vi}~$qenh(y6QxAv&lWX%e)POkK31LQWjV9wN8GC`34iNsetciK z?f0TnzJ1M)xOS@5W`aC;$lqOx0C5vBNp;)BCb=J>xCEUV%{vG2hB80eh)VW?x)#uWqG`c#jbg?`e(q3Xz=%?Lzy~=zja9mzl))=woaj;)#lU zzES2_mRPCyUgKmXQE!%y<8?Btve)eYk_?nwCM7c}gYxmx-YeL=cmC!V-{UiykYnfvM9ynC-@iKb@z z!NKxJM=UIB_if)CtPxn)qC4lx!j@m7l~4&dGike4jd=mQF4M~0H9=j zno_QX3n4+pa|k*>wu6WI2sq~DYJI9RDi{Di|P-o-K zgKUo(#1(|R`>eBJd=$I&cp3HJ7=-oUn8Vg1ThCLD8+s?gt+V-M&to2N^eAi(n_n%C zZ5rm0#GssYv(C2jdGK({_mHDUK~cl}vh_Tb$ZwL(3~0}lJ&$?HDPO*mU-mqPra_ba zd%D1n1%uTGXEfn(LA%Ax%0>nLuh8W5`=8uTS=d5)$YYZtl-wEAVj9rE@ zkC~53l_L`lL8-&w^(erPb#?RGhKo=RKkMvyt!&Zte>dhaR_|iY@1Fggna5ygFOJU& z&STtb!FHQsg5{sG+t(?(*O;>VICmx{`p%SRy?IGtP9h6K8ro!?2rNjo!l03?S>e25 zGP2^lVl@$kY$am;!F&6?#Pj+j%Yz&6P9mNypHaL|{lBL8b;aitpClswe$5NeW-XmV z)E|`%eO>%CHY#qFnF6{l{uIm$%AxDQ+<%Bte6xrwA zDZH`OyK&bXzULI9Px3sJx!@D3m%8%6xMQxmXjtZAc{{<#&0H#<$XqJVmGkZd;}x_t zY6xd07#w-i@hW)Uq@pQF$|_ z4jF<}m3?yPQ;!c=We3PA>tAo;c1Mo8=(6Vydq69nFY*4eO=;K4DZb=9cH(-2sXea$y6#bch2 zZR>1)+4GIbcwn$z%#Uc>r549F4Gz8as5t9poo(my;Ng}pjr^d%p1veaLSpyt2bEKXAvQwjTH6yvU-{ z7K!$xFh&WTjh8*&pq;6q%r|aFr7DrhVDpXFa1rK*XN}s~@sdMZ9t!Wqe1mNtIrr@E z%zT6GOvh)%+o3m^E4i6J_IKyC=}bS&YcxYILWKyt`GZqG@Ya9++i+7&k_UZQxd;>|=ne`JmVyi@%@ulTUy_Y|KXA|9U^Lt1mv z*@X{yjdsaNaxh)Rab-i_52wdWOf%*rN$4u3_3prY;P$@!$~*h~Ug#Qjf(u671?L4= z8rc4$~E2xG(gK-lj+bhPr+mWnH&x!pPj&9O^n3TH|k%Qy{NV&TZd4OGX^bD~l5Zl>B=UikMjF&SNd3g<#CM`!+ zv7)#ar^!UKi%xtR3K<-gLP&l*{tW!hE;_N_Nx2U?8E_wd+(oAsn6-WV1`|#- zQX=oujHpt6-ZDoT`Yt-(K`QQ|BX{g{TaVj#o*DM6oPES*+Os~y=ALDw{5+0EVzZ4@ zkk>}(I3tb8>t-%!8PcWl%u3#aH5_v4k{_Uc}O^mpU&YxJ^ z#&1bJUfvDNr%BJ@jf!85(70^-)Nuk15BdVj(}WEY*wS@q*$%p_Li6&{SZ;kG&P!oU?1g?F)K z?4~;Th0~b!Bu!gTcr5dXcF_^@s;6jZy6_%m744$)akkui4V_c?VP+NWq9Z0_qg`}J z1I8{orjYXsALNOmU36|^Ru!?YB>+Vgg}-Fvr5d@iu!dr^i;ftfjds!Do$2nPBjXTf z7oABg^+mC#k#|t^Voo8AD!RMq-7oAC{xa^{HH}j8n(Wz#-XcryshPIDgbYff>+}TCvGRphd zMJILxBJ9sDI^0Lo*+u7@JpUlO=uE^ZG|Y_i0Jm%o(sx4$Uq5hwyRbSB|@f z!J|c3JMKP)n6IYijr##Z3MJ%hZ~Sqr^n^fm*YUj+rT6`YIy!}r53eu=*1q0Y4Xu(* zOw6(Db1|?j(Lz7f(DZ0bR`TM(fotIIsWX-rMIFbX^G7DG_b-pF9$A378)RGnxYKbJ ze~t?bG6)Uv+H)ZOmYQq?Ux_Jm)Vt6JJR zH*__KT_Wr?Q^R)Bol~kd*0a$Xy0Pa?752Wd+rD%(Hg0eRpQPtEx8EHP@mTtsSf_O2f|uQjv^L z&t}6Rv;R)J>;lx!H|UHVjxsj;G4(ZPW1+IP{;}I1VH2K<8fviN5UHJLZP|CI1GTg} zw^Vi4x72g1T=YNOORX)~Ey!#QRI&8b#Z}8rJ9Wjfvlp&Bt!g>jxTZd;tWa~Rq~r~o zu->J+y9F6XDg`;$MfEio;jG~J}tOf&b5K1U|eQ=-*4>b zYJ)XfZ07{)Qte=;R<+e^((8`<-`j|muaa=61Bk}%EjA!(K#yRhH%dWte$kd>= zb)D%0(l#yZ0SyiHUTaG`E+4-3L_0x++a!_y8t#3C6^zloPIVi)x=^4h*xqeu>VQt7 zYD4>m?uL4~sqLmqc8eslDHSB1v~~8(8ZLyCZm2t1*-e!WOvi$ZX@*&!PNitdcFEtA z$TW-D9*Vn0)wHf}fe~YgVQV4Ydds#+1M=Aa&9)`M(6 z)Y!)7w>AzO7!NO?z4Ra)c)3mn8St2jALC;*A#R{}X@uG|jL3M|>k#Na4?j2FixBu$ zw9bZQ>p^-@k0c7udhAONG6~~9~nqgxu*hdn8BZ^wFjG_yE9JE#{y zw+J2Eu{e}WyYl;ibqx2SS3_})SIl>s9m+Pg=$N*q78~o~B^(XEj4qKnj+w}UIu6!3 zh7Za^(rIArldKa3PGUM>TJc0hK3C|!Q1M*FX2ora*C^hmc(39=DL$!4(hKu@L9tKq zbw#m>i*VW32T0C5f4zCJ+N*9R#3 z`T%8LA7Hh{lYM<4%f3E9+1Cds`}zRo!xQi|jW7H9K$d-dfU>U-Q1U-Q1jRX1eSosB4^Z~?0m{BUK-t#^DEs;V zWnUkl?CS%ReSLtkuMbf6^#R85vShocLDah^?Co*VeH$Y&G%8_Z02=(@t=X3b8Ds~&OLX%(kK z$A+AY^T7^{BwTA20!_eY7+PHR_2JQ;(@K8k_n5U!)C2A*e*yNVm9ywA-%Va4?Op6i ze*C3;@1|Eu(Y;d1ox8;rwZ?k`X~0L^C2OSmN&AqdJqBrQx(UdG=kuY5^pem|LN}SK zY2O?qzk1DYl#}16edH(Wr%p3mM>%J;snfHtd*RAI&yv3nnQeqtD$h?tr`mfnc#_~( znsQ25zdBPjl@Yj#QrB+A2Rc)C{n^6NmLNI~1W|_qIzE#xdgNY8)F$3&a)FEzm4hp~ z9|QfrM~J0}{tlk;+==|BP7?am^8wJF@>Kxs>6r}aLV|d1s6^%4IY^#@|J>yW89<4u zsp7{YilapR921Y{384hZBN;SOq6YtrlNu#zFoHQBH5uXg@c_dX=EQ6*RDUsKSaix{=^GL!MTO4KT*JB?b_plGw7_bmoSO4RK<;~+{@>QF?9DlHo-QRg$`Ri+5>ycNthRHBM@ zXU$Id1x4F^eD>yLPkaJ@x8f(Cy(f4F`=jFYI_O*YtHCv;67?@Qj+Cg>sKuJ}9LVNs zc97?Is*V{r6ea2th||9kRnr+Isu2=-Uu2e{67^B$HGmTJ6n3*Om~>mww zg-X;Nj2S9XNAnD!5>+%(`=LZN=bM}NRpt^ZQGdlV44_1v&6v-abQO7*Fy^<7RGIgL z4=Gfll8_iFQRB^ws!CKQaFnPN(cV;v3YQ^Mi5f+wx?!|N9*PoGqee>9Fe{vG$V${( zaaK{Hp2>@HT&O_%7+b=sN#lh_GSrHR!V>m%E9Mt+#~LV6S%6qkLE(QBBPHsEjC_iQ zrVF<-a->A%Y#mBelXgzwXBatBq8`S`krK6wkr!#?`GxBk8Yxj1`w$}~>dzQ?sYb3W z{3WXpDN(OudV<|t7wV{VtmPQZXFDB77*IE~?ZD^Z04;V4nR zh-jijl?OniL>tHj*UFhi ziQ3HE93|?ZEVrXXeTeBDCF%~I&QYSmpz%JHsPT`W+4e_?`fZ-DBeslVc{J-hmS+$p zDldUtC0B{cd(|pY7qO5BrbL~Hyzz(_Scw{c0GVTJ5>C$Jah#OW5hWP;_@hyEe~eG9 zE8S$ofLgT+JU&s6R*wG)lKKZr+%-4}d)pB-#O|*`KK=wr$yDW%(nKFG(JQ?pk7Wfz zP3vq#dH-u#bprMlG_9lkrc~_w!n$BY;(;xyaOG3g)wd>jwbZfs=Z_r6eHyn1SH`c% zxqR5RT&8suoEu%NuORw2QrLpSyGW1rFBH5}VRTH$giqB{wBmG>4+BZYf6>7^w z0~jepLoFio+1(yASfaPH8aQcb`4e5KRe5KqHuq9y@>E7q6{QUI+eTSw^r)eNPW5~T zpf|TFH>Wp235dQyts=_N-2gor51O&HO|~*r6r`$LR6TH1;SB9>hJY?-E@_4?{H-bd z!aKJ=t+G{-TWz~h%o~lhQHo|My)%@UXbw@3i?tx5eP@FhJ+mqoLxn8VmdbsrLALsOs~K*q&7>@3@*a$mH{@DK1w_MX72`&A z9IDw-!8Yp6oe$wvq2@T$Q{!RaFeB_ED1-AMdNBKFC>G^_T z+@n;+h(jDF>#nn6woZ1Sc+`>DG|Y$bKI(Yb!+BZpeuMWO-;mbXc-cDH=TVP2C_L-I zHn8`bS899!7@vURctLBX1|nE`EnTXF1`Z!YrV zcWagpNy7X-fn%oO^`k36;81k3zg4}ONfn*TAj8YEUUOV8qs(ahTvb)yVFnOhYdy$aHj|9kAHEeJd)n>ec$|9Raw2s4%T?wlD#|wy z(f{sI|7X>Ir~2R28wR28weG7bD zMVDN>+KZr>?XNMG6)68fM8DQTW4nSJ~zMl&Eq$Z zGv}N+^PS&sKm}h06?_>~@MTcJmq7(z1{Hi6RPbd`!Iwb=Uj`L?8C39PP{Ef$1z!dg zd>K^mWzd&#qp+U^Uj`L?8C39PP{Ef$1z!dgd>K^mWl+JFL0fT;QI6otpx0?$@MZAd z(7fQw;D4fd!I!}cz6>h(GN|Copn@-h3cd^~_%i6rm?*HkKWX}=rbF@a;e9?#(-bMv z&DNCmn;5@H)3bH>T1|_2A0VFZX6nGoa{_#s=DB}{@$!7ZKSRy)1z$UjO%V#6>QQHP zW?BB{FucqcHM)w=C|nuAp<~2Wz3V#0y;-`Kp#zvI?)hHpp?cP$r-P_=!b}S}NZ89- z@st;xjt^D;r-(6_Dc@2WGyM|cfSIlV0cLsz9%}|OtwII|Gv!s)nCX1P_s95VAhTZZ8 z7mX#p#;6E0JstC5!xbh@a^mvGOcx>-G1DaKXED>0SopLeR1jD~LZF)nGnIAY2s7o} z(_*HRSch3E->3wSjj)($JL6`nxG{;F7?+8eUW6(LW?Ij<2s4!>U5l9th-NWUn$745 zGd%(F1T&SMjWAPrL#=xGjUpq=^j|3}2WEO2uU~|j-pYzanCVP%5oUT5Q%0ESAK9l7W_mYM_5m~9&U!|e z=}Rmj2WC2iDW6amVp(DwPaezbV%WmuQA}(x)1R|5EM{8G#1=DM!Ne9b#RoWUF^#pA zC9h^;ib0&!c+R320oGu_9;7Bl?}n_w~1k8`+M%#?Sgi<#a<-oZ?- zpfCqBZKN~@GvxwK7nrGFp$=yHRU{M4R2~2pGYwdr#Z1#=yTMGKL$?TK`tR%?iVy5#MYcW%q?OV)rF0Zo1Odn=qi&f|;(QGzT-~_o0iK-p%w5X37l;E@t`& z^LL|o8e4*H>J4W49TsRY)0wPKUocbNs|GWbkEVl!nI7+7g_8V2|F4))({YmHuN@v^ z7msOV^&?>OWO5!YhWC>}Z<#&!@iZMQtT$|}tt)Na)|yK~pN>wF3H+D30?GuHnUI@BCI=8n;53dFBjFhWDBXgQRuRym zav3nB3+^0-kmTP7hwOI%v2Vbp+%%=?QydWN-xq{D2)Lu!YmftX>XC7WZx-%f}7Sl-Z zjN|V%O+myxkH&H>)HtT0yAwZLreZP~ z1d;w`RoU-ss~OAidLP1gDW*p|BhT=Ly5WZcYjm#(Tghw7pK@+(*dC4~>-t0g6HDOY z*k{E4s4rn0!vNcH;o2g9`T_1ta}<__>m_U6gH-)FXJH|TUbH5F_P;Wul3x2A%vA^cIzKdtHWnqpyG%Kg2jf~_I_(dcg0N3b0UIB~^ z`Q;3|^sWxCT1Z& z8H-2K25pLCzeodwu+pn7)BX?d6d939sj^-Cp0^OxZ$pd$2>$}V1_&46t^z>#2P6SN zxEqrh0}wvX7^_ryKJsXQ@HdFb1rVxSF{-gR0O8lcT7d9=a^qK`NLr>1Y1B>wgrCI3 zDgp?{fzbfr-=r?0R7tFd0HGKg?HVAw4gw7%7*Bi^84OpLxWM7^2MB+PTm%STWSt|W zN_jIy0HNr|L;#^!Ka2oEF>x6wRbI+V5dnnXWo;v+%30(hfbe#9K_);*H5~y6Phnjm zfDnwUR5^kfEkL-Et?ogol6sH=5K7NR0HI)u5kUAw3W@+iQ5ou$QsrDUj{xD3loJ7j z_)6#u5I)VGumIutV0!@&p2Y$pfbc?gM-G7STP!>R2&Xe&1P~@zhX^1PD~=IB_-)2T z0HN$;>H|P1Rzo9z@J7}%2SB)=h@aJrS1qfT%O9ldriGj(8jVJGB&zRW4WE1mRfbcY4Z3_@iqbLgyZewB#5Wc{U zv;g51jI{vaa>iPKu!`kcfbe@P$O43-HfaIE|6yVa5Vn%F0O2}Hwg4gTOcx-$n4%qk za0eUV0EDs&(E$jHD7~{%*wG)6)bWklo zxQSvcKqxAy-2jB5zwQ8pOV~&Y5QbzeK)8^?EkO8n##(^zTkHS}5I)XW3lI)xUJDQk zW^4gMekMBrp?uw0fKc`!I7*eXDa!!}-@&3Bfbe?O!~qC@K0O4hfca$n` zAm1H8_!T7R4M2D~D{KM6r&xYp07BlY1|Z~0&OuYEd=m|XW!lx4>-Z`9-{}{D&~Ziq zHOl5KP^2sqMaorUX;#@Un}r(r7mi&>QYKlKkHLvbhFLML1@<@nGbqTfIKiiJVt=l1 zDe8DK`|)37iJQR$%FlBrszG_ayl4w4;T*1MA?*jv?_Z}%2_^(E@9CEs4y@a-S#y>cdZTjfAnZuB8-<~S+L@HHlI@D{m8w{)(Z)YXpuIvD-;jaOBN6*U1_6$tzcEwrZ<*=^#dYO(mX(8z_4+BQKm&;YOijE>1n0KYykee4Fl(G z(U%P$u;7bb11(-jP1EMhG#Xtpw~qjBwyr4OIqtgxvkY? z^O(Sh8^uV+88#cg&6SR2m^>82bX;OE&Uoh`!1x(=A6}e%YZ_OHe##>?@~WLY@QPw(t$5TwrlY$MKd0K>^^j-! znW&qQmyO@n0z56F0PRfuvhmx;oci&@#mLLXZ*PITlo1AM06S$Mtl!7g-{C z+4${4i2R9ZB+^kHf46B0BJLXM${7aKlzS)q7*owA>ibBCq9bS0W#hLUNcTXL%F1JU zHMdfcoTS}@K;pM-3#-a@v8`q-!|UyVuIPMvv@_)z-cUFE?}gu1n986R*^ZSC&uf3Z z!!w?9P~{Z<`%A+{S3w0FO#GL1B>szTjvTK;QN*e1auRr^U4S3cmuAJ!0MGVtPR;mt zX2ompC&H8Mlvls~^_<1b-}IDKO|oUO?Wr@`F<{uw0dV~W&@mWC>{s&llE?Ff{YakA zmtas2xHJZ^;L@NcGd<{ZO$C<*&u1OO-=nGE(%?5}ey65_OC$Vd&F|J!aA|};s(Hbs z!9TBg07N4H_nHbWjd;POK?Rov6ug39(~P|^7W6`fDebvm8se1aF9PtbdH_@kPN&L_fU^&eDlX;9Jm1Qm@M zP|^7W6`fB|(fI@wolnr$@DO6XMCTJ!bUr~v=Mz+PK0)VUOfjA4e1eM3C#dLrf{M;3 zsOWrxiq0pf=zM~T&L^noe1blw<%!NG_}4Wb#{gzIqVowVI-j7T^9j00$BWJ0(W5G;JisSZ&etdL6!7({JeT`!)T64u4tG6z?14AI19>?^!&8 zgimW;o*#%`rg?c@ftTkI{u#>WEamVF8wVEeQAc!EI{$M`mmP&d(6y`+pP)y3ctMY7 z+ix)Il-3uWlfD*@p%welwBwA;;^FOWdq+Z#^nW$>-6h^UaHc7bRK*?Jzh|1JIM(Ev zDdf0J{GPWB)EA&}Frw|5rau#IB4UFUTtl2`N+l)$$SrtCW}j*LQ6$!9n$qrF4&*pc zB?SM4Bo1;+M8=+J`cZP@SF&6n$FT`yi$4W6_Fg2UcY$3Ds@ax8Dx=uFf5SWa}N=|W15&NP+P^BJl} zg^7ER-=1kI>Wk5trq@tPbf&4i=b|%B1tZHm({v?@>G@1kQR}kE@yA%V9*|?cc?CI^ zo{f;>#jIX*rs(*aCZ|X*qkzV8gNGRF{n>f5DzHv4u&QAlfrc8zIs`j%B)M&orgl zT40gmN7#|}Ow$#thDDCIG1el-*RWiBrs=CJ$Rfv2F|kFCi^$qDO+U-L7CHVnV=Zzl zbKYj$;coTZ*8X(u0Pw;^W|eK-S5n>9)l| z9NmUK<2sa!qmnm{b4=<1U>LzoKu;3`<=2S>bOm#oX(pd))9tfnjp2S*PsVPLBfI+ORzwN!Im; z4l1ub9j_fRL##|kj{P_x;#9w$lEtfqa|0=w7iLvMgPaQA+|pEo-7zY-^IHnC*nXZx z%5rFfmTez6i&W!=(C-u65L9qOP{9pB1vdm0+z?c7Lr}pDL1q06RB%I3!3{wLHv|>j z5Om-yQj9nB!@yajsviV5M1Kfw2r9TCsNja6f*XPgZU`#4A*kSnpn@BM3T_B0xFKk7 z&muh%5AeR|w^lC91oEa#nU*e_nNClgol4E%KmC77Iz5wCUZ+loONBScM9|dIY#9<>7A)ctg1TX9Yld}qoCl#6_A_rxj3F%$O=btJ@9{#FG;NA%H zzsJZc@h`%h0zQucG;v)ZK1jy}b>RGXaId5kX~Cf2>niXTkf0!F1&J2~e8l^MV?JLj z{-mfxf9;^pFyxep{9mE=AO+rg!S$0>F}=*=sp}}MX)}@5w_hj!+qlu0{$r2ld zQ)~t!Uuj)fY_b9}k{{3_j_LXVLqPqwkJKGw#?Se^JNItGq=)k!ejD(XV>;D34>%)_&puNJcx4YH zpBKiNxa@DR4Nm>K`v#-BSjq?kHS)5*!J3`=b@vVSG6YZ~);N0kyG>IQ&ZAG=v0$>? zi3Au^%_be8-+aEo&O*9;zQHajKtH1}euE9ikNwPkNAohi!8ivPjWFX({S0rY8-6Ih z!B|i0+|m!uHy6ha9s7yjTxxre1WCU|FU1K}`i-W(pqOF|I+piZk9#Uni0r8Z9nbWj zX-#Ko%KMq&3pHJ)=?YEH*R)F04Vv=4V7`kry_6JV?_-)?p~EFT!g>GEUJ0)~(@FjD z&rpsPhQqSvCCyXP(Kf6e=Wb>B+a}?DL%3+}Hd;n20@^2cc&WC%>#zqa<}DX{BHR5P z?HKx3hTgR=(iTaq{qrx-mOri(_GZo7_r)~qnBbAZxnD}3-A@0XutzI|4H20WA`Ruz z|NH3N^$F!t3#941kEe$98uoKX@54np+ z;%+SR7LP1Vpx*C*2le(E^Ve5A)?1;asN1@TXUT#w%ndSadq9?C33 zKa77K!vkf1%Kz)IBQPPvlu+v9AIP-F4ztWj=MX;}vSf02Nl7@QWO8XZv?RQ^K0P)3 zKuLPj;-%POD220Lbg!JLsJWi!l?z{fjiQ%-ryRFO2FAyGz&Li{f?=_WVezZ|jP)nJF6w;Apr6KjV1Q(Phs; zQ+RevL^zg3`-qP#&p~cOm@DqwZDc9l*o> zGmdHLbkT;qh?M&QV;OXah zpc{r)1(za>V;q0?Yih#pL^_UXg8#9r29G1Zw1wqUw>m z7L8knz+(_H3P1KAuMhhKMMdrv1e8XcmFEPB*OWu%P|RuF@`)R8Own=7`rz3n>xO37 zo@5HfZ-^4OwP*W{G?gIqIKr$8G<4WepF4?J$Vn2=Q9oF7b42OV0gWp z_6wXv&z5_dE6Gca_*qoXM-GTN!?ARRu4)6>DnVBVVGzJBB=IKs)%#f&yOTncFuHfC z1VWEs5-!RrftXL4`7#p?XCjt5LMI!fk{y}!PQpy?s3bCGQOGJSV9S`y4q zafJzif;Tof3jXCnoM)s(baRUwvWqo7EEfpzJf0t1y*sKD?9b4CgbqCJ^?Hr8D% z*qXk3C)lnP79f1UE`tD_NkpjcZyl#;K!~4iZ3JkZf zevtwL7w!6THr97oUZlWq3G11|^xYno_k?QSvP3J(i%j1g#hOJ53>T7%6c}QInXzZn zcMl?2rUC=YaugWI=BdD-lWs+Gj`}kG`H?6HxB&t22l4Mp$djGWffxUQ%j3b!-Nu)q zMxwwV+Naj^-Abg5R1{*#ce!jl`8D5(Eli4Lt2KRh3Pd^z3`NXqP2XL>uCfXY-(^Qy z(|58X-zqRHWvo?TsA9R+^xdT_$SN>A!^BpB;ZIC#P2X)IYZVyIV`8hoz&q15eK&)m z9R-FuHo{S0c$M*v0>dxJcQ$=@DjVx4FuVt1M1esb09JuvG>f;U?=XMMPk}*{sH_4* zEyY*`hF2*jw*o^<4DLA!3}U3vn!XbSWUIg+`yQ+U!>8CIR)OI`cCR&kcRypT0)w2b zW)&C~vSK+E7(UIuvCH1*Ylv(VHyL_puI zy5-b}h%O?3AKuuLkqN$i_up!GdoJX}J!E7eCf&F@3@>8yBfXnCqC9 z?o|Ak9&-$Fxzfq3L#H8Ox?M&H{Kh-a@x$v;^_Fi(<4iiv3!Pw(w!KE9@@xm!5aE@2 zW#b|fu{Ji#2tgX-&BjG0;$6#jQ68z0SMB71*IS2|y1QP_bab4Cy7fy#UJ7B9hn2HP zUMu{j4)A(6LEaR4v@>x{;9YqYPW?EqF!jsUk9Y!+DJ_e{$m6`*mA3}+I45EKhT&)A z-3UMPn6dO*$a~Z@QpYj=Hth!y_cg5by8U9xy%Vt*E6qBYw;$=o83xp(LvS>v80}+? zHlMvYlK?2uMiuwwa9NjCWxLo`Ge+U{Zo#-;Nso4>T*Djch93$XBNJKhrS7;i+WCS! zqzsGRV%sJ;eB_1aVCYX^NmyJ8|A|YG8ppbDPHPJxZKDmKpI{!K zCo>=DbWKm!l+P-L^VvbVR?`id?$q=Xn%=DGZcX_m$9#`!`n0CcYucgd?==?pg)$y`K0{&&qi;Xt$VzdKPY_x%jjW$rR(FQ6u z+CX2#!-Mq^8*QLsqYYGSw1J9^Hc+wA1}Zk%K*dHIsMu%&6+Xaf}+ZJ=VK z4ODEjfr^bbP_fYlDmL0c#YP*b*k}V48*QLsqYYGSw1J9^Hc+wA1}Zk%K*dHIXe;(h zvc95g0V=u{prUI5D!LY+qH6&vx)z|KYXK^{7NDYQ0V=u{prUI5D!LY+VxtXIY_x%9 z+h{u)F9Fs=Y_tWKsc3L5i5nR;4_F%G-8y^I2X(-KWCq}j{Ec4{0X>3#40`S-2JHL(6aQ6c!aFjpMw9P z^s{@zvbMeR*W9xg?HXDPf7~0n4{bYck~wn>XM&9f&$QdUk^AuuJuW@sddN6uKiWLB zJiQa!4$j$odHPmFU6({z$MZa~G|n?ypwpq7j$_zE>BnR*;2`Y%Yu0C*4Thif`ww8I z^p8Lu@*Uu(YnyOVf8<{PnJ*)JjCXy9u?Yt&ocq0s_HX9QGyZd4*iTWY2LE%S{ zs!MowJ)YzSp1lRR9eDN_hXH~A)XOY^0XZa?v@GRj|2c8{8R>89iQPT*X z#l2wQ*>6i-1fIPZUc$4FFgDY^ToKv|i*<=LjEdmdjmTiQ!bHm9@`qH!dI8VM(Pt4ndo-^`AK=;R*#8kc zTShK|X9bXm;MsfFgkIQ}E0VD~C-EK92%g=}dgg#JnQ-6 zN2G)`UQ7^}p5fWgL;m3f&vG6r@a&Cjx`k&?VJ{gAa{nU2eC*4~=YoZ2Co-RfXMe@g zyMbqEwKj0z*&%GCg=fFd>=vHow9&CIcM}s^c=jWdW#L)cpK##WA2Zg%vnNqzglAW><19RTHS5rYsGLLbR0Q^^KSQY*J?Yp!cm{)VQSP09x??%KL~_;DTJ-`E5%E-WUc$bzZi_dFk` ziA|o8{^I597ni0ttpE9jigamMyCfV|e_6QgfgOv&<39E=g&JcJAd9Q-&F@X)czG8L z8*#ONS+FbI8I!b(V>r{vK3qxMih=QGj1apxHU}YUflLy}GMMPz7C{i3>g!u^U|{9u>QV+^uTZvT<`=r9$in&IIc>b9!Ck#qLYNyehKs;>V6vXW?);60ah8`{Cu&2`^s5 zNXHpA8!yh4j%%$Z55+LuRZf8M&dW;o0$#*?BN}JYW#h%8pl~x0jbuBxp3C;K9ocyC zYP_V0n;2)j*?93BfSAlrd89^OHT)(Iyxyw-_~y`~o$2Rnym$fRrI3d5y2Fdp0BhN& z#+kC35a4u!SL@V|^A1zLY`plrke4#TK#jZ|@VoLFAdmPR>xV3nyllMqgNS^{G!p43 zkH6bA1raw3cwL!cKux*XcySZbq3Fn&blG_ECy_26y!e~If!iRYC%kwK#{K)EQf>X5 zAn}^|kU121F~^2=3Lhp;s2nxl)SS`q#FtY}yb4$U4+dT=7%#+g-b(xzw_W5ImyH*5 z?Z7z3FER`$`HC!lbrxR(-pr9%o}LKf1)l*f%l@O*`ewdkHdR*DynpM)7R=muifCpO z6rk&>S`k&*y0H;`Qoz6yeWm%Ib~5 z_!C?glln2>iBqRQ1Mqw<^=S73c-8<;jea8D$9@AHz^PR~4B*tN9}ZWX`ejTMWZY_6 zfEO$m3KN_&J)^>G1b)+#??zEg86VTJzIM5iidZ{4={*CI7R)+PMh}uR_+r;MQs2)_!~2-tzKWI|AUf)Ugdyupa~bnb`3#;I|&|Ut+<& z*DP2z^>5P}NA?U}o!*svUE{w4IB!4Wybh8(HWSz#XYD#J9yte-$rHFBmC|r8)QZn6 zWT>}}!;@EC!@VoT?|Ixl<%{a5hI@IQm4A<~bL9T^+SCJdf+VMEC@b{Fg;NA-`agE?!feS`bl`PgS zbrHDtCU^<=UI!sL;NB^Wis0TWk->0*h6KPpi}DG}WJQ^w6!abps#l#&Ve9*tr|8#l%L3#gzQ6f1iP z!9sFj@peD)6$-U*?*i7X2e|iR$SZKK^lSw89z{v3RfS@SdKMSKy{t>`v~m9v`3d*> z%o%Cpa*o;w?%m9SE!_J$a`po5eJ2Zu;NHpX#~g6)-7NfW)xgmSev(^l+<#_sBe++# z+eL8iTGlUudj&J<1Kj&pmL9>qqF0#%?)@msi{RcG)-%$^eT`fM_s(anE!_J9vOUAS z+|+k?!M)sRAaL(iHr>L#b9prlpc_kyo~B{r$!pmchAm7U!Mqmkl~?qc!S$R06pu=Z zQl5o-k6~g9_g=}Kvv98fffnvfGuFbr=dfH0_uj_~YqfE2U}6jRKF7os?tL#=3-`)C zPYd_*&UCeLH&e8ujk}!v;lRCI+#X4{=QDcF7og?qow`t=3wm0dv^?%l*rIaqM-f1`moPV8LF zb^K4FAivBnLJ%Ei6h<3&n&7dk#?;q(zU;{IFC0^|IbxQNscKnoVpfc80j%W9Ti34` z=eM9w{5h2BL(3<1Hrb*t+YBE^aRDl^1Tou)?eTUBS+xeDuh z>l@(%ba{9mErzN$Hgy5Mif*xnUFDY18kPrJwf3L`r-#kCV|{xn$`chhv#hZIXFfQv zYh_~wi0UBmStzPhMAoa z;QFFzM>d}Nk%Z^XHbRijc;AyHZx1?({Xlu7MqV}iCJ(&cH?h(*i5~5kj_yMI-1fPr+iJ!#yxw+< zmv_*ko#_|D8|sGtz3|iuQ<>=Mseprtm-5*`{1UKpuA{mb;C z0}HxA^E);Dgr+xZx?9uxHGNdmr!{?E(+*94uc=_AkS}X}pt3*)Dmt*Bq5})sjDf@Y zhz>01RhkzaSn#_wf1jq0X(~Ffi0{z+z_W2zrT2NwMyIT8>t>l%B36_!oUTgSDQ4<9s@&lrOhQpYt8e9&LlwpXyi z)F<~5Clo!=a^QsC%Kg+ajiuZ6BD>aqg`OyQ)Olzu{rQfE%J)#$6*{TU962X-YllB) z$H<4$dyb^e>4enwjx@qj>Fphpz(2fh&)!K08{=*7n20dS;Tack;EzU+RjTLwqpjf4 z)IV*9{wdP?skXhuFvAh37tYc^U0-7I0gUn>Vw9QZ%H_~S)rW`}tP*C7HCFj$-VSm} zW4Iwa?+$_b7N_>P;BCL`CLKtC{H&aR`R>`?S+z;m`Qmjh` zRvB8XlA2qg#VV=$8TNoxUW1aa^}|)FXCtigWJ+4CDjQ4OPC*e?$-0E+vxCCo%YAg< zY{-b;h<`WXCmnc#ccec&#-JmSMQ?Fpb7ONZ%_JZ&$!z!D- z@T(Msku(fT0O9T;>GBvY2ZwhkCZ1T$0wS!EdTn6_RvF&)1(7i#@gVy#!YZdSUt~G> zaij?&tg?|@gjH^1{UWUL73RypD#LH764Jvex1!Dut2&o~L#4m1>NzJdg!v+@@+#Id z1FHpxu*g9 zBz~Cu6q|0b%CYPvgGR=Z!(BF>B-|3k7A9${I?Ti>LyJ{XbuhG8WeHh}RepvYX|YNw zu7(z?B!&@MtdcNOXtB!2IIJvIc^4B~tnxJ`wpis>vKFgc%fuF|#0A2n7PZU~ZZFulbpsYDoBta2WOTdeZS>=BDqew!U&vC4-TYq82B znb%^K#H+(h)zt99K%nR1<|KEkgchr$I&bJ;l`|>JQB9=+Zs=f@H?k%UR`~?QIauW` z#yeQ$hZ*l+l~PqR}F7FPK; zG|(R$E<(o!N;S0-(;okeXa`hN>8HCLKLcATBf<){oL|I`*R0&>cDf=~2 zXuSvA;ik}fm{~YhUuXiC8XZ+!VWZoMs~73E!jBY6qZJoV>$xH{LQbL3x@1PU4Zq>_ zVN1#6aCphm$zh@d=fq8)#zW&S!ufGaO4FyN=|edAl5ki_>Hn54Ub=L$sJe2E$;Da; zm>S0+06o_Q!$wD{uCfNjc*b!;3;W5``*w6*@4!G(ITdeAgy-@ew8 z^HPsBLtC^`8a7g^cG=nUmsT!cIDggh6^qYZShwsX`sum3hNv8$~f zi-F{Rw0*3NC|6x_aVTM!EK`V81S1a8>-kL^^KyZ z5986bFdSLAY8eJP0R6fxIKHs1Qc(VW#aq?A-!~vwD;%50b7j5g9-$HWLbuWp0|t}( z#b9z*_LAKf1YU>Y0Lx|IbT0sqDMuLA8iCH>8wZ*$%Gpns#L4wF~g_zSjsrI^)g8YhC?P z_J@&I?c{;idmU@fi|Emg>FBcYTI!dk5YGBxSvr!Jjn|d~EM=b>XX=-Y*IosAtQX~R zK4R*Zjn{61ycFV%GxByI!0A@+ddTCNIOQQrB##>&nTOYp?h}Z7!ZZ>*4=*UqPUAF$~ok*7tUi&O?Sk9??(tq8FaetvIO8%R2ouE$Pe=ofD`lwP$ z|Mhl<$MEwWclgK)&%wlVS%Yp4*#5{@8kV|c)pii8_v@;Uq*^} zgKHbgXCuSw>ze8%a!Umx+PD)*ZHt<6q_cSsXd5kD`saN6`+PKbz@z8Wh|C0ucG* zSbGDATm(*Ox&l2uABkfriYy==n*%-8B?SM+BK5hmK#yspHa1pD0(wk4r4~JoO(!ep zajXX8EOufk3Mq`mIGI)GalqyOSb;Tc4D|Rvkt+*5R`O$sOBi*k;^K*o$ZI%d*w}FS zqsO0t45G(vs98AGwBrXXXPPPXX~s=ArGAc;pP{(I#2$1im~YNX!b!5=npGA+SvmbMy$6HyIyH&o?iI>Sm=rPr6EqZ(rxd=V}0_zu{$NYHd4SKA~ zQ-+N#dfdQz=0J}hWqD7ie9IEESl*M0t4REeT!bFiv7Qln9Eh&6)^ueCb(HKZ6g848 z`um;m2j7DHO!U|$CJ15@zYqRV%rQK+1pkk^2LJpYqDUoId|IwOcQz7*TzQ{6yB#$W z^!PqDeO5pr*fWr%dSQeQ$C9VHtTJpI#TF)4GVd8W?}+3nkl~=mN#>oaW7EkCnb@Mo zKW0ZR(6MF7M#ft7csXM&dVC_U!4jQ#LGn@-WYJ?n_zrq}KNFv=6IUc@*w{gj*D?#r_w=>|H|0h=yBj0HYR#}5*vALPz32510Bq6(c^^_Zqegi zOl;BPkFW*j>)u|J{4!%LdQ2sC2R(i_CFewsKh3_h=<(SU<)FvhKI5Xt)I|uZrR1=< z%}-XaEC)T_L2(Xxd?w=^^!N#7{1~xt<+$p^UiqaY0bS|8GOKajV6F9yxinwj zcBj%FG?din5M$wv!0zFq-g-b7nHufVo~j0A{k5vK#+rgv$M9k$^5PoU;%ltC*b_s3 zK*Mo5uctssvwBGgw63bf>jPkaD2*6nRaIHJeG8pSE02J~0haAGmKqHpV#@=NYQI3L zUFyKPF9OU1#ARcuZFsqG{f+M)%mgAV?L^IEh9T}lni|5iblI3{u5`pCOdjTAI&KCw z&Uoh`!1x*W7chM58E4`+M|6U{d)N*%lI`HSBHN1>rC=~74_I$@0_&GffbqTuajv|4 zbztuWpgEC(wKM(9DXA+jUme(ALLRS?ai)Gv2yo@)s{{KQ*8^;XVnfhg8s>G=tFo{4-I#M+^Qjv@D)PcPOKb& zvx68W;Ka!BIpK~?@@moEAe!o>q|NnafRl5Olsd3%OMq)BYyZHFSmS(jV8=5Z#vY#| zq^E1j=LPx2nhxl|s{W|KO~HPVPe#x$Y5r@P@)MZxatRspxru$~my0atTAF%gH+N=PN?gz9y}ob&SYaw0zzVSw;0Q0|VWm*= zEW^b-dL+QU5Qr#)4x%1vU$Mfac=-7QsIsua|3*-kSmCvJie+Pki?BjkckB~Z_-Bgg9ai}7sFh%a(z6j(Naf@H!wO3w zEl;fQeim%8LaG+^87rjzP+zb@pLK{dO;6)>i?G74GOnMo!e-Vp2UaM^bPlZWS9rZx ztng&k(_)3kqoPLBl;3hWu)^;l+2Ms1QrB3p!f&&yEmpXUy<{~_Ik$?~PFUeu=FN!} z9?QHID_qaS7At(79ci(`iy3RNLQ(I{7c1b)#uI9v4lpLea9cSYat!VKq(Pq?m57!k0K>ax_g( zWg{(C_*-VTSm6)~w^-p7Oq>%dyo0e8E2M>D2P>?gyAl$b)flASy;@F061HhumRy zqG>LqueGJ76yO99zR6Qcrc7^csnaY$zZ!$?^*>f9pC$c+7C!1_z=e6^g|u6Ss}EO> zuPcQYvRV=}2LDwgF1L1E>@jWtF?^eyNLmj~JY4(Amg!|sy{+0(1<+1Z5tXju)<&(8 zD*B}K{~b}k0|PGgt(TXAC)6c-q1IXHbeQy0C*{t{w-9tVfEvzt|Fp?dPWI-G#Z#|} zdpEoD98FiUg(|Tnaz~X^x3

yhUjP#hW#I)!EJ4AK;x{@}h;MibLj?NU1f@|WUm?DRizn_y62lcH_$)G2u0Da1FN zskjk|PoOp7EX66rH!M?}LVUy7ic^Shc$(sX_)>4?WE2zk!}*EhSeFG1VxNNX!iD4_ z#5a65n*laR)Ua+#6uT4n8V_{%IU=QvCwL=RBZc^e3h@okWe^36hU;oog;?V26m%X9 z`~}7F1nUx>&khQUfn^MjQEvP;{QDAqTKJLv@EC)R;K|B@_=a_fQOr}%5SDQVx%IjX zh~a<=i1>z03*kM2n#E^;aZ+M4d;t47Og?BQeL43onDQ?)X%IbPA zyo(CdLGg&h9+VK?t@4dd%woQK**8J)m;@2u@V>J}N-BY#gZcgs2sa@?#5er6n}wU0 z_$%{0aF1{b@eRMFN>GSz_^_(8LVUw-tMcX~hB4ox_enm5_=ey4o^bPrRW?+6;rEmj zh4_X~sC>&3A7j2J6<3icV$FUy4KD%!hKbGOe*BcQVT~82P*EVh^B~n{hLLRDNU|fz zqQ4ix9~#7WBABC^G82-vjesTKSA=a_?Lr{XHwdw!9sLlSlm7+$?ux*Bf)=4ydb{p zZ9#mYLb@Mfj^2*6g7}6S@eSiX!*F*dPhvSkKx_v5&pEN6IZHvS*|ef&Ea zwUZ;siTFZ=lwlOpN-+~OJ2*+i7b>Jbm)JrtP7ifoDl>E4Ru80Nho zxJqiG5MQW}K7wWh#T$}^?Cc9Q+noFr*-Fh`k|g3At_zaVwcGvVT*|5nP89xfKS{(l ztd^1m@#XfTutu_n#kc#(A5mPb=I`>8S2Dg%^Y{A6D;Qs|`G@@EP2|^W{xRQ+vk|-* zrQ43h0}u&-__6{~aDf+};qu3L$E*ea3e-FW@r4R0d0wPnu>9s=v#>yX>EVM*!~^l= zkmM!M{Ak`suVAPO;)~1lYjonVM0_EeWqR?i<9}3>(cW>qd;F2Sy)kGQIXuQY=s1pE zgd-RszmV|nz$PFxtP6^8M8U{w|b!tHuHV^86)5j957qIPg+QzJ})hm$B(E_YWKY%6kFoM(l4K z=M3QqA)Dd*-Z#Mo-hl4uVoWfWzjst1IurXKrR|c1dACFx&XxHGK1gU=hygt>sbMb(`y2*4H)GZ0n;jJB!>AWgkF<^F@RW z8h@y9;e&Lvx*DSDbn%N@HWC4s!^i8YTD(4NENH}`)%8lm=B7q7tf&=JyRlX&$kY#< zjgH*~*mSU|@+|`Y4#2+s3H!=f#eDR0zC*`#M)zG{-VoWiZ^N-jhqPRtWPYZ@#6$2M z@|atTt99Nh3^44e@H2f+=~#xz1FuJUUFnm~%SyKwFF3yWj59LMK!6kM-GX-HqsLl@ zdGqkM4Rz=A*~@ zu95lSzLz51I3-&CoB9F%i^dcqZb7;SB!&8C(vhk;kP3hN7|&aWz@Bj5i5M?)yO8Gu ziPw}v=1}0i?loc0^V)J}wQ`Y1*n@%l4%a2|+0m6At2;J1o{M#Q67rL?_!(Ke23ZU$ ztY!+E>o(VHXz@szxADE+QdhsRp@Ba2de6j%S3)N7;mb*}A7D2r#>eBNxWPuF?hKb5 z=@WQBzu9WBL;{ovZ2Dn$n;+<2Pwa zOK;>qt|`yoCI3TBf2-+hnhwIlk?|un9k1zPP5(pF7c~8orW`ZOcci8%O-nUBL({V~ zy+G4VntnjjD>S`H)30iJpQhi{^jS>-yh*u#(lo>XmilX2tm$c*F41&_rq!ChU(-)% zdb6f?X!=b}pVRb3O<&V=Fju+I{-mZ8G@YjDJWbEhbe*P6nqI2u)tY`m(|^+RVNHLe z>8~~YFHMKXRrzOX`W{U`uIY7}zM|=Cnik+8%6cC~iZOPyrWfh(jU3+y=eyX&iP7w? zap8?vyJ@n9cze``osz#k>~nZd!DZmWw8NY3#ttv4KJ@>*XCu8>>c|o^{Y!WHuLJgV z4~>1@(>ci0HN`OVymIklaR-6^3OG27b!GWH=h^1_AtZ-=-5HEJ1on01+|wLqo8O9-So^x& zoo#+MYa7|u#R zCKsv0{wuFWqz;>Tws{-t7pcSE!}2oJVZ#h{*f2vKHvG0KZ%*PnEH6@rZRYjPP=^g8 zby$Lz`PtVU&ss<7umKIC8g*C}>!`z$#d^PJ3)2*6raG)mT#I^*)QR&^ht)}~I&35g zrRA*-`%ctI?CWxt<(zH4jJ;&lVdX%0!*;T-dnEJbw6A+CMOpj0$1|~2hrOB|Y3=Kt z%2=xod)S<9zLt$})L~ymw*egG94#a`Dw`rYtHTOj>!`!N3u45+t~>y&I_xhww1@b- zgak*QV5fAW4m%#{1RVVpyUVJ>iq&VU4*Mo!bF0G!`%$VvE!<+rI7< zOl;L*sh)~dtCC^=Id7U1Z8()#A)r`fkw9d-ppIqI-~rYuJt zwvh4v2xpt~Ud3hl3C7&97qQWOQ-{?Lj@;_7!Tq=Z{@|Ek=Sm&+6ij>kub_N?mJCuP z@XDEL?HE9#GbDQD*qcx(UtY7h%-m?kbkf^c0sT){AXjsgtDb3Y27K<9Y@qyFeocki>sbByj*k%)_ki0cqKq zr%7eyMO6)3fz-mv?j{e*uUPU>nCr!I5_dgS(6LnjnxlxQ*#IcFvbtfDLavGKZr%uJ z_N{B8cfh>v!7#7eB`#TR-UXnT5QxjhC2t3gLp*`+AWR7&JnBTvW2PZ)i>8J!EnPM) znJZlyX-yvHW4e5B$v+?y-+sm!8QHkxy=(^>$#!skk?oD+w;Vr{2VO6Yoz)+50*v=P zh;xeZ9z#blKjo1cdDZZnJn(vV;bkuKI5KdipR;kvry$Stb9cDpvyex8#5hw{HZJ)~ zr+%DInEHJjev^kX{(#67J=&SL9pK&ezTniaJ6!TetoPn)8VMPc$KP$5f{6PraHBHA zfSPi%^-=$Zbot8~|? zS=0a3G>+#8^Yh3g(gFLqsvrJQ?B|Zf3y9Z!il*E|gFl8}9KR9xjlyq?x4naBiHmgSUo)`CkCuP5edpY&_Sjt5 z++EkUw{XPHeWIh;-nPFmy?x&>uVSx9z12Oi#|xd)kUF0!{HRO1ZWrwJ?&^p`&y#ru zAx$CTo6D6xD9hxJwtYv8xV3|NuuMmtQQGu1b&9bq-gT7m602wOy@L9cRb1QQrB?0J zI<)&^(1-QP+x9-#w0$4#1mjW-6&+Mv=CVDvc7&+=oIN``Uf8#@Bj#P(@nZGPjQEWZx@e?98E z9BIzl$G*Y7nxQ4{*$LfOo}ap6ALZi}v*eA`GwAc>`!Syz%J2(tt~+qt^Rd$k`78Fb z580MiPzKLgW#7CE{~-9;PV!8LU2+|-LKn9%jkbIEp}#SNU-HuJ`yWp4+3O>GQ3}@{ zzc_v|{6^q63cu0#rSY4N-vay|LVC6>t@}$2hqnFdS}1C4F-!Y!U3l$Sm+b3Si_XtC zZ3EJF*73EvyaTqIk39kh<>GYLL5s27>9{0Rdr!h7<@geKLebtmsqjeuD8C#JsWF3~ z18on29Shby7nWJhEL1@!6)qlWvYjwJBU`G(v#Cs)qRh-Dc>GXZ1Yy5osgsoGik}e4 z8)U0`BFfCLDg6!|fJ1D@;Je5x!#|7-xEMrx-cyS4-U+|Y5oSzGe;RM_!OFz+ZAb|d z)2s_Lji=#0GA5>}J7r8vL*e)+oH?u`T-G1fIP9iFP>#}X45rMY-w z7%~{HF!5c8Fq~F6UWlL`Oia@RLR>KH~QrY~kWk%{TE85fzD-i4Ngk%{RG(8O?N zk+gqALNNbXiW`-vpp?kOG$&o*Y!x>qv4&DIO-$1Yl%sHbGHS*?g=i=olM{vG&r+zx z)W3yHJz?r~C|S;|m7a|-^+A-hT2&~P7{US~6Vt3q*ees$PoWTE>Wi5(GBF)eYPN~# z^I5PpG5tH_>>5+obyJwSV&VxJf3cYQYm}7(Q~x@9?rxQDbmCLY7h&pDjklP33AxC` z^b*R9F!fbzLN73NRoN9w~b71O?Ebj@GZ&^Zs&LWVtOc9i>YsBM_Lop$1&Do z>T+nG z3@RMs9hQNqf0oi5Or0C~pm3aRVp{AaIGFk%h!IR(9sm|o{~pV+CZ^?>-+VCj?d&d# zsTZ>q7E}LM#^%P?e^`Gt0-)th7H)j8LyY!L&6!?iyzL0Ta*nx8kdn z5+71hQA)YCSPeDha*=rM3F|$70TcSGPJo*7LMe2Gm-2tZOh5IRcaP*x`Cmjo`TyaQ zeN2Lio@Z&NvQ?k*8T5h#E9={{=D^lK*d?2`NrKTeQ{h-z!00@$*Rce=O@S zl8$M%kk#p4`~U2H34B%6wf>os+~i(E1g;D+Tu4wrNG_A0Qbml4h#CN;K8sdkt95?$wYFMoTNG-mPp#J4)}gO~m}(tbUTdqZzWV>Zwbwr9+>ijG zwZq^2%guMzp3gq}jNe*oYrZn~du$GYsjZTu4BbUsq3crSv#xvEu)7ciLvZP{J8TIj zV#XhYrz6+x&B%RLuv5>lPtHD*S^insmdDCZ6)KkOP@zIT*L*w*6&3E0BgR!!_{+-2 zj2%O$s2Ed`9q;dX)7aXv$BrH~Iy=rkw_)t)Yy~o1>^6+|@4tYi~G& z`Isa9WVv5l?$0mxOUhw%?w?T(lXAbVVcdvuFg*8{mygK841NDw)*k7vzhL?V|D09j z6SCRW^^G-0E&M^n=wpv&xwFwU0vhtqD))Pp`@eTqm7{q1*fIN!c?V4{fbP4CnX;W- zdi-ns9HjTL7FOV+s;d29VOLHK3=VZ`H14&{w1YBQaatwtoogeS+>}85^)S1X0vM?bQ5?5vSIhMchrn) zH%!0bt9nJ_(yUXxybi|Ai)yV4b@)~n*?L%C2=CSz`L}FwOXK{-_46{+uZL%&2BV?S zJOQ@#IYW?h%;=$=9@}mG+aEskAl?CLJhar;IZ?xGPn$N{5z#@#!aQ&g53Y8jN8_)6fMK;|IX@x#C`uUoLI>wz~kEn7@)?0PCjp6zk9Ks2>zx^Zw2 zPDnV?wZ0C>X*#>+@80aC%nR|vASyl8EwYD=O|?8XjH(ruF5r>{)by!tu~D(L3$U05 z<`H+odXN8STLU~CDa>$liQSP4e7kPB-J3e>{QRaRutHt7cv(yRJW)o(n9ES(VZ3P> zMn=vwTq@vv0+*Qm$Jdytf@xFTunYsFr(1(uT9JRTjI*hbQBIB zrmlX;IhjLe9+MfH%}zLL+~_eQoI}ZC*U*;^+#+E!56Kf}RUUJclkX*5r@ZByMN4aH zf$%ONxWI0q`f^;JmpbKgB9+&+v|zKocG>*un%deW^=CWf%wl317dJG;<%Nxl>*I36 zlA1+15zWhL7b6N)MwQfHcjq(eV|AU-rH)ekre|-3q~35x4ntWf9@!!BIf2hn%*W@6 zVJhQ^+=Ot$h|J5dAAZaqCWjkLHFXlHS76IfW`T6nLq5 zb(|H*%l*KsM7STFb(D_BgIcDGc021RO&$?FS8@OBxYT0X_5qvDQs`PxC&mUl&Yg9X zDp6k>GO>SnZVBt7y>+a(b1UZWONg_?ZD4);-J)pY59q&ZI!b3D->&E=eF6C%vX#f$V%xF9 z={$_erf_Z{p87N#7w#AL713dMe~9Dk%fcFBn>I=?9{)+|D9u6Z9!8eOA_}f(70n1E zOgi(LVA9*HsO4XY$<#{P7L!^3+cBBxYr>s2_49qi1XKUqaKK%eFZP|p_Ls8F&tq%f zQJBv9uaBj({nnVw^vy9DVi^7&I$4U5m~ zw1Me$Rsd2y1HXsy3JuAi^KC4^m7B=t8Q3cvk+?G+N!?V@JVRJnx|%uJb8kfjQQp{t)u; zTZK13^#_5dTIBJHJa3pjRq&CbH1RgQ^9ss{+%MnoB5I)KVW~wgB6cd zBo)c@I>oaTzp8k*;&&9ER{WJBorO#Niu)_l(w^}%73V67{vhH-e-J48gFw+A1d9G3 zQ1l0ZqCW@}{Xt+KTsT;-=nn#)QCZfhfc%=uBypK9A9IGt=Qd)Q;yA^ripML?RcuoH zyyBILH!9w(_>kh`ioaBRP4QjD1ST-s@2gm$c!=UO#gi2q6vb69%6&oQn-uR=6#YS@ z|4L<9rvmbODi`5gVY~elhbvB0JVNnA#d(S?imMf`QT(dnHx)N2KBw5O7$i));}s86 zJYVq=#SX`~!`#YO>`z4h_f;IPc#Ps~BJ$5syh`J*SNTT8TQ&Y}mA|g|U5$TK z<;N6X*7$anUse3Q#{XI6zbew_3Hw<{M1S^B?5FV=mCF<-Yy6QaAFX(S#;;ZR3o36= z`6k5&H2zVQA5(l;<6lwvHN|Zj|E|jKEBbhsVZZlOEK{WI1mn-(bp-pGL#wh05ove2L0esC>Q3H>!Mx%J-=JW0jv&`4yF4 zQ~7O`-&Hx_exN^cJw|^9sGQOL!|BqEpPJ?+_{7s-HBWc$r)JmJFL@vDY6v-cr^U|b z-%__PDETsU3a>PJo6y-zL3fb4f?ewJSwA%Kr8|NiP7=p3FH-f(*JM$2E9e7dwItoQ zAX7qB;Gi3Z84TjA7dMKAI7juWKytEIEN?2xI|*}W52pF(fT^h%sj4Bz)E?{v>nhP~ zc_!PQX@^R7J&m>2s_Z_i&X9+#k|h? ze4N}|r6Z=}7t`HCVl~fp9y3*%%gDClj6|f%(jM=Va6~gOfa;qpY7qo^{6vTaIwoSO z)--|`P z^~_SR#|(6j&Pt#`ml8F!k&J-?SuwtF`}>*tgOoV2P$`kBV>K$JQe2tbN~%FOQ|jmq z{+!Q9g?s>d2^+FUm``s(J_V(qL6?%n5XRd5SdJ8boNe`N^xYqCq(tg;wll#VY6_)^M(UmV25XsQq`s;DW7+{m8j#w?v;&QlaZ2__6MWvG23<-tb}AW#0Z7f{ zULH<~8g!|C+&GfE0ntlc!dj;ragFB`9)aW+>^@@2jw>NIpe<_9nUOt-QLLECkN(M~ z3qk5OcIcFABrTC*U;I-!K)PfH>n-Gy+HLsB01kGzWCx@6_!<7)f<~c1m-+zPegk9J z#(%Pn`Pv4K11FR}lv$dN#O5BDC1Up#YS3vxvt);sGa7V8NThCIm3Ofqmh4FF!Mg4? zl3lVx9wbXkQWvtZdri5%sdt$3evS>6>`1Mn^zGxNmQ3o`%=y6iB9*0ju!Zm3DAMp$ z9!K~gI?eP-?UEg)1-oR2>9bw3!?ZU!bq8yC^g$^%B{haax#`Cu*(EznEtM(JQF+{y zn~{2$eR;x2RjFR==Tj5-h*df(^$Kfw_G#IN*^Vc7s=S_+s8w_+#L-hPioIAK<4t8i zj}Ok&pc@IH=UKU#Dca1-vE7>`dITrqe@~8p`yx7E>Xj|6cNNn)CQ-X6MJ19#?qhh} zz>K6STPWl5%ze2WaiT$YEq8CCXZF@fFGW0bJsk) z$vivFGZPwgJR^NB*%5H#Y>y|KXwdQ17hY+eM|N4V<1r?D-a8WG#E%Hi3m2YU!nGp-peCvb;9dlEI?lM+>iYM=~Sh7Pr6P>D~JuOXLQE1SWNam_^ z9?Q=0)=5uhr>j}^G;b4tOLqK-?Vhe;bNUdLtyXbG`YdLi>-CYL#gZMHDc5-Wii{;Y zAUb}nv@9BQ3s_s76qhACPG;NlRK}7Wd`g4{9UJ#cv1G^9Y_36NEZM=gQE1Sabi8sG zawm8)%CPlwysu!`5(S0qKp0)%6i$rGShAxUvMkwA#11rhlzEUYqS)*;iMYZk7Qm z<|}K<;0KK?YJd%p^r`_3i?9gC06ELA3I+~Zg5})ZVQkv18eD^X`}n8_-FO#mn-53M znW)rr-^1f1^|+W7e;k2tLALudp2`2gAOo zHpaQ*`)HhIHVOx!W=ki!*t+pB^(NS#Ccj*Fj++B)p*;AWAY; z>84sVDs***oYe=SbwsFKf?cs3_>AKYJGy%2;fI|#^Ms>LI=p%o;ybAyY>r^(9eGqk_P+!HU z8W|42xMmTBl-ihLQka+Tb~ysR`5RS+96h71G-9lZkkvLa+DnEFFkP5mvuIHbzUa?i zjIB=E6W9;*&1k!*_K|3za6J&!vv8K9ccOK&6s02t2k2$R&V=o?G*qRj3w57Riw-AK zsM9jFaXv=@gvbg}f?8CYqxEGKa5}4(sb3qrOpbKksL-4^1Da>*G)Gmc(Ea&~m(}4! z)R`B$$%&L_a+Jx8n$5hHrs@Sy=b%oHXsdB7&C)zZ!^0|^*g=pJADN*J#9iv7A*VVh zM)OR)^caPnPc=nm9#dYkbm@}D+GR^|rtWeeC(ipjA5a*^ZPNITaChn`wMP=3USR zn|3fFV$seW*pFTDWitWa;V!Y2pboo99xGnogBU0~E<`&nwXrhTbT;Di8~4oyJI<#g zB3|D{)K`Kyjx)Z*h4t|e+dg2^*@pUfEN!r9o%KYXiS-Xf!ul@8wk^Xtdf;U+!wn5K zZ8hY0|9%_*zeTqJ65aseKgkL}+wNW1#$0K(Nu~I9 zOI-pRY(DA>*g3_tLy_+xn+a(15zWJpNn8kDHYJGUD@+gk*j0`@_cdPM`Is+55od#~ z&&n3#@t=gA2#;kH;;pm)oH!RTI7SQ^{HO=OkFPdx+`{J@Jo$n3x6gsj z+2NKM-vP+XYtK%(W$Yle`aB@VpEyGCGm6s`k5fEJ@eIXfidQLKuXwxSw-h%k{zUO* z#a9*op!iqCG=`h~p-B^wpS6hd6`K_4gNyN(E7J6j^6iS>Qrx8YtRlaUFy9{(`SqGI z?R$xR757yJjF{D zA5r{?BCUg2-(M8@<(o3UFcHfXxu`1TUn%}Z@lT2Y7zd_anrDI~5;P{E_0%6+0CFq?m^Z%yMbPVTzL#XDFViI8U)f@fyV&74KGjNbyC* z*A@Swm?$vq_EFqNae`u{;t7hiif1XVR9vsvs(7E`M#X0o+Z4Ac?ui>5_xn)AD#c3` zuT%Vk;$IX4TvS=Ex8h(T=31$u_{)XNdmi~{A$A~Rx4Yp8Tk2{ShZJFErySvgU0=)M zeUM@ZU2=pO`bF7zO@~tgza^P3chCn+BD+0j$O=-HSA_4B1oPT_;1%f;Cb1)aF_BCj zjOUL}eTDp;`Nd3Uryb6ZIVE}glyICQ+faE$r1?aw39cO;iZEL zHr7iAzX`{@bnr{Qdg*wFDcyMKFr|VFDvEjO_%6iAOUEWkL;0{R93u@x)k_DzREAzU zhC@&<9ps4AOUDnDY??M_xMCQ<`YX^-ey^T0$=!2U7~YbdZkes!Ar;M$o9|WrQ=@oNI1rJ4t3*{cr^7 z+PIiVi$@xpno9FqbN604=-?pcrQ>pDj=XgIkpmfd>9~QZk(Z9sm>PNMXyFlwymUOt zX%%_tpecIHO9wxp$Gmj#VLRrfqlKxFmkyqpaW5UKS#``y$A#R9*vc1ESzF9Y$G()e z_tGH_8e(2L&OkN7G0FuX^3p+lm6(?f@l~@cUOHTMEArC82l<$n4if%5^3pK|+2bo; z9LM~TmySIsMqWCqSb5~7<6fplUOJxQ07PCo9%E|crDHJ5MqWCW@+f!frK62wA9?8@ z;qJ%0bX2mgn3s+$(_>ybZemYjUOJv-bulj;S28{3rQ-^w$GmjhKzT=AI_^P+596hS zuTL>A9WS%}-SN`FbJcq3*ub6sJ9+8wX%|+o-|z8so`SX%x(mV)YBTDx!#@xEU(omp zk`3W_CX(EJ-64o(7>}P4j+r>&n3;z@dm-UCocWb-91$ZN_x>IpvD`-Nt9z)_$@=)1 zW!*{~FdWbQE?7)3l?O-z#~ViT0MkH18O^D&1`d-3GACePLI)GhWsp3hQ28mdIhxjy z6=GCbVOdsur7Y<7C$;Kb5#T)X0bCn7^6Xz^E7;6$hRuRZ}kS$dd!mLmd5_KUH2o#HGgVMU)Qu~ z5y|9oI04u#inL2x`uMHF-atTw<#1!~kF(@S!{hF&sAA$eIbjjFHmfKfnw_m6kA`Q*!lv+))$z-iOu@^D2lBwkAZHU!d6f0>#!BD7L;pvGoOttuIh)eSu=@3lv*lpxF8X z#nu-nw!T2I^#zKpFED`#%>Ik5FHmfKfnw_moTlkw>kC?oSNH43}xvmA*ut0K=lYIu4 zk`vmUjjen7nO0nuTlXA-)9hjA<_^1FgqwZ2V+`_)%^v2t*^Pm_@d^+7TXZ8i;I?Zz ziZefb^@9?3t2^exw!rDxzIsaQmfwBwL6MtzxuZDy(^mthSNjJgK~FDxVM#W7SkDhi zd@KO9v4d@+E?k9AXvYv2xmYX1&Ai|7LDt*KIE+;hw-3VhyQpt8Fk3QpYa7@m8u$@9*^000RWSD@ox<6sMhA!<^Ew2n4RW)G^a+g7fOf~azmtM%vwbCrG zcBTnqKUx?qc5p~)GVUWq)~GdUjapMt7TRt*PvVEUfDp*>dt0_)iwJb_pyk;05Evg6 z1fOI4jr=#<|B%Ye09|Ah8=z`9TVTNB&dv9QtRlRe>Mequ`og_BgyWKv#GP(z^b1XQvC(!%q|B=67$rOw%x5$Qi8NDVyb9*Kg%xP9X;UHFZ6-YqfoF*gHm&nJdj|Wl z0-3lUyx-dWn2Bv$2Aj^8aN@nk4GlK!Sjf1&hv9nE$8lhNL|Y%<#aYIjzgXc317x=m z9kz8wY@6}dBOlLu8|?9DKt#OV*4XjDX4vj|*tUIT9h*@f$J7R!wj2@h`tFPE2k$C& zKja2s3j^OqeVLeS>$@Cr@%kQ&^{)rgY<)Ljn`LaDdL!Wx$Tl#IzdIFe{PW1i=Or7M zhk>^obg5Yc{M!Q$E`zK9wE6f>iF{$$5BVMqr6?cs$B)w`*r_Rqgc>y7EzN$E0J*R6 z`hJXi^^u6P!M1B>_huLo?=8!7rbWS*l|kb& zk8~8eTH7;9WqrNGij;A@2JzkfpjZa!Yo}XH>~2sdi+y%~~# z6h(V+&AIFq9e5W&9Lq9E;+y#*n8C=fovw5L?HZKYtydZBf%1d7`DdX4X-^%Yy zr*-6V-))>(xCZhvf*B;&iTh;RoD%M6M0q`s45u?j6*PE7q~XZrU5HfpnRy-CPz7C! z3#s~<$!}qG{?LNUklD{ngH*u9IQ*`4K(z($ePppZpnhrc3RLLR>mQBTlf3o%LuZK; zIKfzovQk@6gPV#!tO6-e@c0|zJ#k*)jg_9tN^P&8r+vK<$zL%wt^?}rXX+1So8gJ*ETd-J#{E1M1yrghWzwXhI#(u^h)79Z>I{n`Goll6))n?ltB5 zCeLEWLLJaQGf$`kdJCmc2lPhvFVq1oWm=97sP{e7g7Gusec$xi_?hv3VA`9UyPzQr96O z@m>7mVV1KD{z3jbF5}IHx>TP>kDS1JDt$lqV4}xLFxS&ZAg^b&JcIP4xR^*&jV(+~ zrR7`m!CJN?eE{o-R%+|TvWIGFHq8|ky+{Z2CmhI1O`Vip$ka#&bQM!09Z;(GdDAs> zW%>tfDAEC?`yDS@sf~J--f^0_Dt!mbMk}>ZN!*KcKxO7E6W?9pXXb2H9n%3lnL82F z0X>Jc#dJXXQ{G+&bU*G+Ob2uds!4eTZdxv_kq+o_>`kE@Li(BE0(IUFbU??Tcg5aN zH~kA{jC4Thoy?0?Y7?8P?pJE_PGWXlsm;SmZNs>ek)N4%RvzhqE@SRU2Xr+vM>?Qy zasW=%(Vmv3XCyDu0j1ui7wLc=&YE*|K)ustZ8Wd6IsF{RKGFddcPB9&&_>o3(*ezA zQ?=5v_?h8bu@}<;-OTD@I-n0SJ*ESC0n=kTpzA2_KnK*j85wp}2h_t#Z4a=+P2S7| z0H>B%?CnGcbO@S|({`ZK_ga+OhN;?*=PC}H z85)~*^Mjv$ApO{V#mH-od}kFECG(xNTHI-vZG34w(`=#p^XeP0jj_dpA_f&tZTq@M zmXGm!uR7ZASMJZ8?)RDQub5s@G2Ty?`?DKHXYq;lPy9@~NWRpbh9ob*C)n-2zw$lI z1|HBE#et}5@V!`Ca_GsGE9%i{wk)l~>|JR|+UH-U??6k^Rs;VT=fb0dbxC6VO+@Zg zKKxhJ>VHke$ld!CEwj2p4oNxOrF5%Ysm?Jzp7oIEWrn^v#KnnqFw)5#igi>Y4pJ~} z;N&Fd%ey*lfwG=>byuf3e_vI~?NlhMt=AtY|D8kMEKTwi{+~+&GRHI0KSu3w5mwWY zV_3b2u85#92?zWfB~AnQFFLb&VGTVFg|2{V=FvmHx{8@!4{ugjVM(To)z+*-%tu~! z2+MqN!8x?tX~7{J&#u=N60O#M_(DSe4vPr!-hjKad_&~Lems8hi$5MeBn-iS=9Bvt z@D+)T{xOYVU;J=`*=auV0=5h`rM~lG5q6WDhuvnrYTU?p7qh{p@nts_?W~6$8+Bax zSrDH_!*BNFJZu?kIuGFWe@-mIZcay9yuRx(P~0!p2i1(QKFf_DNd@pikDn#S}m>$YZdhT`LESDeS!kO6b5+2%VH z`F4f#Sb==}{9uFK52AV2F)1td0wR*nM)-6s3wbXaY<*U?7?0mM z=MnD-+l#kIIW}-;z?Ye7TcQj%myw9Ac!JB0l0vdv7TwFobMh z^ap`uDvSOgWYHf4ivA!_^ap{WKL`~4L7;qg0E+$~aI@AU`h$=~e-J48gFw+A1d9G3 zQ1l0Z2V;t`KSwHx{vc%09|Vg2AW-xNfucVM6#YS<=nn!#e-N0$6k~nzF%&5JgFw+A z1j;uZ;9Sio`h$=~e-J3&i-4j(2o(K6;G3FX^amk}{vfaqrZC$R{XwAU4+2Gh5GeYC zz;`sC=np~`{XwAU4+2Gh5GeYCK+zur&c(%>^^5)>Q1l0ZqCW@}{XwAU4+2Gh5GeYC zK+zurivA!_^ap_nOkmb4`h!5x9|Vg2AaI(di~b;F(H{hg{vc5F2Z5qL2o(K6py&?* zMSl<|`h!5x9|Vg2AW-xNfucVM6#YS<=nn!#e-J48gFw+A1g@rDAW-xNfnQZw^amkt zQd#r|A-Ah6`h$=~e-L;WZZ@n(^ap{WKL`~4K_Gq9GW~rb<|Z#S!u1gmKSVLZd5?5n zUw6rmOH*_G;t#o({Brr?joZEoG-i4T`6w>ooM zxBTsc58j||AVwPcchC{cv~J1Z@JRfW)-8(;0Z-@5YA-+;FS~jx^90C)+e8ulz(_u) ziu#0Gz^k*9$+TvOi+opW)HLgd&wxAdIy0nQh4`(-<{-cgRl85XR z$anTtokb@LZW47CqfQ^~lIKJ{Ql?A3(drAvNyZ>j9{iMV#9AD?vd+a0`9`jIQOspJ zj_O6l$c&0uHN>b=I^W>wC5M$P2foqBe_WOj=`P{5RDII$yPW!@@sS5UX?W)b5lM$# z)+bFXQwj_p84NAh8)eid&Am+NnvV=jso)7VnJD7J7S_E;HlW_fCk<~OF`qQN`>0Qv zL($35C(SPruRdw$_)LA$@Ohs;X}JD{ALk>j->v%4R7C(Pa57tBcC*HGcEK< zGn=&>X!ayy_((pR?A#~K6Rb4iBRLcNo$!&Dp=I$&BO@E~kz-iX$)*cIasev}`AGKV z!}+B78ycZcnrB!t z`zHTLDdZzJ^Js*8}SYF2CqZ3d?bs<_(+QQ>Q25OxDNH_@{v(y;UhKk zuJDnXHR2<~s?gdl^O3XBBj`drm0rhPk9^W(I7$&8*)uLC(&8J&rl!)PSoUC#9q>v^ z(j$Rb_kuhM*S&a)sfTK6Hq8g$m`|FkIgpW0nj@GR@sYCFNW@2uaFH7Mq*=>`B0iFj z;xV5z-(}|GwB4%oIEoP;IfR)bK9Xl<+$YUsRvqIb$8Z2*eB^sfkMWUhl(*+2sg#a& zFMf&I_gaQ0SNgFMpEPm-i1^4hcCOGJf?CK&Qi}!aUYy5g-BPZ5aUiyO1m*a@*TeWX z6!~P`i~nN%5g$2<xt}h(vtk zYVJYglV&4RBR=wZrbc|^VAh<=M`GOz-pR;Eeu-ll@sazosu&;nSJoBdBimS6%qPt( zrpNfm%UE5Ek35X&F+TEerpNfmS(JCcM+WuCu&aC|{Kllu<-kRJWZ;l#nzdo%)E z*Px4J#P+_OO@Ep7-Y-qFS>9{i(@bmsC9R!cS~J2-C&M!?@;>u5_n6`!vdIUbfQ75?&7 z{=(@M$5!}*AF4R&=%Yt>&7G12wSkAevbc9;ab}%+v3HTbHdvF_TN9Z!T+$7xqwr90p`bqK)>L1h@L zX0%k-t-uFJS^FSb5X7$TU>642ZlRCdRTwNuP1;k|DA-l<^PiLU{MbCl?3^ey)3`cx z7-JR=*+s80i<*}$sW*mEAHjpnr~D~2U%=Wf+<$ycb?q{&tb;jR-LPzNouenohhj0vu;PA*V}3kGN$4&gxf@%C8JX{fScKi2+9}@~xIyynXoJm1ZZ{U~q_AUr z;N*VbX)xT6nb@{vu<6`_@6UI|BJ5^od$u_kQ0^D&BieDP#kMVjP3TGC!x}d<*l{Me z8}Hvd)R#dV>%&*Ou)Y>-$LqTm^^HKB4K|J3ZoIxVvHoF~!ul@8wk^XtzK8lU+|Xdt zRzr^0cLnMzK^*&sB4K@B#5T+D_%ZwniBuZ4foc5RqG;pq06E1rvLD3zdah8vRO@8Oe%JU9dU zK#mu~9R`l`kWe!7Y2QwAMJG8vM_B&oPU$?4Y+(9)D*!3;ys&}taw`BSuf&gi9gZLS zdlAqErkkg1D9)7`D?0lC9`e=PXm+Z@hf4a2!vnfR(*pg>>YBQy=fvsI$2iqxPmxM~%^g4vr_fH30KHOT-B|n}`X$nYfp6g)jhQ(l03^ zGv^1_PJfMvdHp64*TZc@ygT&9v>1q!f%C+}$;Mf?Q~n(Xy0gvSc$P!_L5jRqQ$AKv z_;<()R9>Sf{5#?|sCRA-Ah6{5xdf-+{uv1BHJF3jYoi{vG&c!n7m&JLCe~G|00j z6~%uK;zv;iRwc^!@_gP}%XJ6; zEb{uxd=0u@DqQVNYYe7p!J7E}Y zBvx})@nESrZAP{oXDeozI}9lvjb~s^yLW)8z27)y4DyuH<}EM3fFEhSyOAHZwF?1> zynK?uYFk^dQi=>sa83l76Y)PmHy6PWCK#VS|A7rzV%fLN!LkeOCz6|x0xmmsGcXt^ zEtNW{nUsds;J>V|au-?)N^gSb_h!w*$khaapNB5{y?(=NB(uO}&p`LSg-vkTsc}Gm zBx7I`R9t-~7;000kP@Clr9?{XbyZ5G4q!2rl*^teb=EYETaZsi zDR9}TGtobPtlf`&k>ZcDtuAHScq1iJw9@k@7%7z!;%0v%m81@4+C(GuP6?4a$w+-u z?=kHFBMnHkvV{YUlyOS-M-%ifPcA!kF4HO*g#kzje{eV@zjURWq79dSBzFU%ml9rh zx)IlSPT>*A_k!KW!A{AQkZCAOF58UkNsMB}v|RL0HeCo(G(7fCxkl0wDfY!bl>_9L zUgBZ^S#Kfl;J4u?131{()9n#3YLB1c->GO6Tz2aJuF|`#6MNuu3lE__}?=v7~5L^`=-yvw$}fFX>W4sYbfDA`k<7Xl6svjZ2GZChZR>ZL@ke* zS}Ic`ne%Z|ZboVub3S3Ds?;Wq)l(C2ukcD|rLLv)?9;LjvmLL78+kn|QQl<%F<0+J z(Jq-z3f-g4B)M$1)AOv{%oJ_r<=F1c50h8qaM`!;t`9DIDKj6csoC^7%skcO?s}!;(|a&;rKV0ww=(k)9#^6FN++jj zmy7jP%q3__`b=h?u9+*-bC`OxrcO&A&0{jd+azUYq_;Bjahka*{T6qj%HuJ?0Zh~1 zFbKj6B=hVv&rERHJR`-nR`e#$_IR@SrB8b4OIg`D-t)-rm%f~r77dKgdq-k?{L;3( zG^rDC*;^$2%{&|ej&^<_tM%OuRI}GAo)gv*voYrGEQzt`jV_eYG9aM^EhxJq5du^PTmfXg;we)<}AYMA?iq^6ugZty2p4bSm7 zkHBT~d(t!HvInuY$hP(g7C%`>EJ&|n<+EX<&9)Nh!@@GW;IcOXywU~fAF}M}DmJI7_5m*2q^>Zk5p%shGPEn* z^ygT(#@koqOWZU>$FG%^#kQ78UEs1=+%LV=O_#9W^HjdaP2bCedX?{Y)9cw>gUS!P z>6cl4zRGwNE96e_WRzj+=Xk9cwnRZ8I}k=gxqV_>?&I{ShP;hjw)CXQqs)V}Kl{_{ zHHo;wDdYw(TvGi&r;t-}2>PgUU!F(X$Yt|f#bIj0Aok-TTBw_CI)(p*|6xyHTg$V@ z?Zwj@lZJ^s0-izrIeQV0XvDlE@sHzwA@yT9x9q&2AuhOVS=Rp3#i1g^rz@Q-Su}ca)8hKBS31#G zl8?kTgCeY1G9L!Jm`EY>T*{SB8ohkSb@}Zs?gKH-BWIA-d3}@qh{ME1sKYa|tY2cf zDREtq#2HC3|EhD0@TyzpHG&grgyCEcJBDvqah?b(tS^9E+hF)leDA7RxU_LmyJ$wMmClW%}p&Zebo17XEzL` zL*5T_wQ1Szdc#EF#-eziDVt*m0{eQOSFmZdS@Tum1`St~DFysV{uUNz0nY0_#e zn~j01Xby0f#6Y;(BZ|kb+2wj*t$5t(=l{rbPR%=_mNEb3$ zR2!eOGFQ1mOXn4T)Iir7(@W>2d)6Y?e1#5|nVG+MSsl*i>V=Io;;mhHra9juTjQM7 zfQsMel;%0!!0giQ0* zEz;7zrn}Snh2@6d{fD_Z1*HeZ1CPae7vT4fjBXR;wglW8Kip8laBUwva6#&gAImcz zWj=IM#|;UobX80_mJI5{SCw%xlBfjQM|D==ubmg0r$C@F7A30MDUl^oxbf*VvYM(yN zwi`012OuNQxt;Qin5e=t0*5j`aD?J#6sIX3r+AX$8H&pkuTtbkE!M-gf8w_kH!J=` z@nywV75||4S4A3+uzacF7{&RDO^RGfmiaDM6laHQU*&Hp@{<YuZ zmnuG@_!Gr96vf9F$`#<6!18IuGDUgOMf|T+{*B_F6oWidu9xCa#W9Lg6pvFpU2&1( z`HGh-Zcw~a@j=BODgIosL-9|Fd6>Y`f5l;nlNDzuo~Srau|@G3#TymxR(weDMa9<@ z|Du>EFzxnH+(&VOVx{5IifrF9>|-gn}-@#BR++y#5zh0w1+ z$l(RK=7+4;k-4S=YIOZ>l97OUXAz@+HspGN@TC?a5IN(WxhmaO^1`Y z_BN;L(c7HtwYRy>bGPDDc*x1ZE*G}Ac8!p}DC0Xbx8RD6v}-$@s!dqm1L!>0Q3QKn zH@mtg{r#dWPEliL{0i)C^RP|3U)u1#z9aiv%TY#ryp0TQgDDh^ z1K=}oTh50Yz`q)vVl$#0^7l~TzwV$!Y~ z+-^ZB8r;5u@$o|kz`tNSp#$JvtT}W5OeV&kV0x5F(uJoVIsgVu2$IsI^-kW$T0#fF z!zqOhfG=b%xekDbq8Tx`&zeF9 z!2ijLLW5iO<--}=(%CZ&Zuelx(BPJeP=03zzz?zE$N})t5JQrZmK7_%o>F4BR z0n3F3w=L{vjssvnbO8J`+k4!Un~}Vb8X4U3%#0h{p2MnR2Dfv$6ETC^gIQb5;C3M8 z?G0{4Un6F4dlae>l2R@Jk-_agY(8=T+?S)U1B2TJ^iD|1HfD?rZkMo{$l%svr@J?} z^+qFm+~BsH`6H5&x`Z)<+Zn7pGPu2hnInT+QQ3$b0Ee#waU?-JBUerIQV z)(#}$Xik019m61Ew*=yZUZEI z7X0BPD*$buI`fg&>>?leU%SXhz6?Tb3K}CH$?;}ai98l|E@M+{`^IoXgYBP{Eym+N z2|hADXEtjBKv2K*fc)8*RMd=61J)rWVio$Us{za8RGD!VzDT*y6(#4h%D7KV9YM!zmqGt?jR#|K* zA@h33biPm%#g-B%wv<4zr36w(gXtZLVoM2GY$<_B&!mejC1kOs1RkmJVoM3RPGzyB zgiNyw)+;6iK(VC+iY+BjY$<^$oJh<^GKDC%l)y@r#g-B>KT|PXY$<_aO9>QPN}$+M z0>zdR$nPJ_|5rt^rG(rECo$v2mJ%qolt8hi1d1&s@Ey%3wv>>?mJ%qolt8hi1d1&s zP;4oIbMs97VoM2GY$<_aO9>QPN}$+M0>zdRD7KV9v84oxEhSKFDS-)o%ma!oB~WZB zfnrMuoTlkwO9@$QDS={32^3pOpx9CZ#g-B%wv<4zr38vCCGb71Pi!e6i!CKkY$<_a zO9>QPN}$+M0>zdR*g}pHD7KWqYg86nO32?-S!^jGKc}+TQbHD6N}$+M0>zdRD7KV9 zv84oxEhSKFDS={33G8f3IS3a+_GdrELlhf1?=e?}>jdG)=NHP@eFw_FA@X{}c)r_x zsP#?v&+anU*_WNbu8$WmHjpLYI^jonOJDjo2EXZKt`xt-GwA!bzg{qw+GIZ z9jWY<9f@bI>?pxDx(XijDqK!ZXvdT+3cyV!N{kIUY>hJo!qjY5F>w-QK1$ zZb<1zPJ-z#Sp5k9fA24N9}L`p?nq0rlxj6eKftu9?8H*WQn&XK{1PXsObA0-Hp@0H zgot#Td6gQ9n7rrt;OJ9>B`64tk)`(>vg8-g@X}8+ANnBXMV;oo)m6>sT-Iy*Dg?#E+nXPj;I zf0#DjNQq<{_QaoHq*U^D42QqJkxG(tC`~j{@8r#_Ws;HlCb``HZZbG@-pdvn|zs9no06m z#(&^kk;;-^V*kFgL8ReHp-3LOPb6z(X@nqOY{g%#Q&Zg0r5I(>LurAv#w-1 z$0TYOrCJe@;68@e4a~@&$7Q@mabKvXCj6zio}K8Ky>-%jHUmQ`k1n+Oi;IagtbL=@ zRQfm`rh~O?Nt*9r;4g1ZaHx8xlPr6vre@QpF!NN8yGtWWD&E1!(zH7%-N@7XLyfD*%|3)*vaEGb5(j8#VU`-ghrNP zE4{?~oMfJz=9vlpl4qnCSx#ovXL~%^#K@BK6a3|K$Sy{fq8#vf?{EyEPb16MnEwLr z*OD$qmK^OoQI+uBc2u+1O5~k{NZ~K#0+4VShqE*NCw8t-4k77cT;<7Mn$f3`CEMu{ z9Dx6O{Rsa?pf%wysZI#~l5wo&P^ceV z1W!g8wtgs!4B#)>fiSwjNlb{#FtR)ivKU#mvcpXtWezJ25^;r-;07;TQvE<*`jC@pb$Udjca%o;_|ap5B-=OvF0KXtY0P zFX9o6m=`4eNBE!kJ^pcS*?I98GPwDkykCxG!X6{>1oRj5F;UPrNglhPA7ijL$p%XO zhoA(#IrIp!h~ImU1^7P%-Axq?NS=?hf`N=-{-@FLfIddRA5PwFp&KkrwNB2-x0~5|=Sh&=kI)?3Cl^ERE zbhhIj#(F&v(>(&xZka(m1cQB!5J{{ffRmTXrouk-veunS8mX~ctwO#SD@wv%y-8X7p#<`-CtfWYsr-fS7(j|QA;Ip53`sSu#ZetI2a;}iC ztwNpTYv&O$4>%qkP9&b^JGD*4{KwQ5erKrSXhqB09-;CvinA0KD7GkmLGg=9C$h6n~=lE5&V!e^D&J z3B&w*DGpOSN^z#*Tt(6e%zwV(e=2@kakJtJimxg5!3oE5!xRryJ zEB-+7DaGF^zN1J_jI4(q&xk`6sfOkm2r6iXGk3K!$2E1sZOt9X{;Rf-!F?^JwH@t2CP zDZZna&o5=DKdo4%I8Je@;_-@e6`K@4ueeU}CdL0${J!GTivOedJH;Njp|br$6pvNB zSn(RgZHn(G=HnvHa`c)+9H6+r;=zhD6i-!Ls(1krb91%gEgJu<;@Lbukj{HF={VdX zM8>dn&G+*4p?npddynGAzUuOe;RqZ42c`aCZ9oD0Leha*iw>v9azZtAt`z_Du z)gCm1GoA}xcuK2VhJ5+XtSz|yj|?)cTMEGaIwjYcb#AlQbp)B!ZBAxwM;_v39a_|# zKX+bR(0p+luFw60Dc5zd{O>>b;K2M&<%7(1u#vs4!^k!+g0QECHn0r!Nb|dR<^}%)ba?nnI zK7%Z3H*tQ?AhW8?T{*9f^#<WwOf5>4#s3dI#trTC+gc9W!kgoo8z3+ z{)-PjC}Mpj=yz|{xw5srqTt_|ai{cPP* zm~GusRMNTyL>-8`liJ_J{&C-(DX6b?ZAZ!awH*(WW6ypWw#5x?0el*B{~n0NF(30Y z@27mc4=ZUn8_t8yPUv$PLf_C?GlYKXfEhNv8269gemKs|Ctdmv7_!|mzI=1R1qC6% zwFOV2Mv&D;+@JC`R*qQD5cIDiOGMB=f~-o=AIgM0|98j=f_@2$#R>Ynp#}4iInNOE zZzFSFHw3+D!RLM267+ecN#1tzLW2H6N)bWdkD?*yzlhsso+ao)qf)GqNE;e=s`~67*kXMIk}Yz62kN zpiiQGsxky2K~EKyASCElQwj-sQ920;`uA8ahoBF>XId}> zeenG_K_C3U^mB5O3YI}g(9?HwkVDW1kC|F3llO8wLW2H$mJ12`A?#U5&@Z7B5%ljs z+y#RE4%Gk8MbPu19m+vZrElS`M+E(O9Hoe$UlbP;Y5E8bQ&Z^!ST-W)Kf@{`f}Yf2 z5E1l;F>^%F-@}272>Mf)8WHr>OpOToGuUoK&~IWx5kW7`=p%xj&wxQh&@W}#h@fA@ z)QF(xnHj_g`g2)zjG(7EZ4e{qse}~72zpX}!S)1wPwq~Ppr__VDkyN%a%qhS`hD4a zM9}{)#T^jz6VSWjV5pnsyHyYo^fU<#B7%N6JG}#fejKvH!1!-?pnoQS7vxi!KO*RV z$jT#vp6`=EM9@FT%n?BkCL7nOkf3j4YDCZvVA+VEAH|v@f*v0QoFF3TsoWAo1pO*j z6(i{BnL3CO^b?pKBj~SVPhtf9k6B%epkKrE7(u_9=`n(ygnqCig8r+>u&V@p058bb zvcnNU{{q|pumnA(YCoQT--5*{;m*1&ScEnyC<$vco=G>znbb^ruJp>VV5 z)Sw?xXq9ZIqLd-&hfrl92ub=akUqIJe;XIWT5#dQirU7-73Jj>3ma=I=GWC#)a<|y z^6oJ^FgskhY+n7xK=(T!0P&h^N&0#94dB{wK*ZrJHZeEEZV{Up?&_uIG}pJ7ZLZ8^ zw(#JI5|~tfcHHs)xzo!p9X)34{{D*cisMI&_W#={_rH5Vx&K38^Yqnc)Q`^k$@0e4 z6{9PT^7p_Jz&BmcfVF|=U+SN`s$v|teEuuXj;-)BKljgk2+IZg=ag4ej2ky*)adMf zqmWuu4)s#M=_Y^vOq)0GZ@eM4Y|xAA)t(~FPv@%WXHdDZM3AJ-XB+s|BUYU7brZK}K1AJ`2a z#2-=P;2*4M@CoV{(ATsoH5gaB)=jZ>65Pq9vGHDP9axiLjodIByJB&?3|rtPu2rYr zv%Bz2EWWpm?`h*gTP8zSZkO4wnuW$Yc#gO0kh&Fz;=5*%EnbA7;UV_K^m1%w3P1RV z&dWcfhCp3Y^Esdk&c*KTTuH##iNg~)9fVU|=EE6To0GNrlQgxCJ4WBVYqwlkE9_-A zbT!_zaGt7xh|6~CMLOZ8O$vKYt%T~|YAB)04_sRn7 zryO2xVA)2QI-lBaL(l6I(i@21Yj8`J&{=Qb$B3AQ6!NUN74gz$B*KL4=L$Cw!@w4?kh*TWv!HrV>CY%w1HkJ2wlg*zDU&EH4A!0tEE zcY290ZyU#PBv;SCYY_DhN{wjZ@E`nE7?^*2D4F9hxszPkNempMbTw| ze1yu!D9%z`pxC1L1x22xtmihxZz?{j_>|&{iu}mV{M!`YRqTcRVf+w9(Pe-vx(q>Kz@57 z&Q%m$2FRkz02EyYpy)CHH)}r8Wq>TY3_#Ik0E#XH@K9W2Sg+_Z07aJpD7p;5b2MFa z86b-;15k7sfTGI)6kP_O=rRCBmjNic3_#Ik0E#XHP;?o9qRRl}oMAsimjNic3_#Ik z0E#XHP;?o9qRRjjT?U}&G5|%F0Vui*z}vN6(Pe=ALzP9B0kY^a07aJpD7p+l(PaRN zE(1_>8Gxe802EyYpy)CHMVA36x(qXIFI_>X&>7-G&1uPMVaFuyfspdHvA|+&ae#7+dyjuw{R@ z`DXaqR$KNe!>K2-|Irb^m$yIn(f0f)^VK4sv zcS>3@C8@XIJVfrC<(!SsiGaRA$~ghL4D+@!9rx=ZraMk6e1l)Pb(`9{OPu7N*XBCs zhxNgqcl*4yo9RlRD3L*5va6tPa8rk$o!7?toa{Q`>R~J2PQTvLc8O^_HMgd%2l#&G zbEjO?-uA%KK7>sn99_)IhoZR!ITR-ULe;W3)_wy{adH%_^nQB>z1)>d+y;| zS$9zK#g5)r-`wF&d89qqcu`v-`=yQ`b7Keh(QkgV9p5R7TxU<{c3dGk3&S86WUpxB z7`jf+c05C3^k=shOj*;G$lRF5WvV~0WAm7sp?ffM%UtA7wcUs{mDhEo+E&4bz?ydE z53(1bpBKG~sn*|1&1%Qv&B$9H!FZzmQF~(lGOg`B$E@CR#gEM0}TOFO=TbWS16)i7-Dvmx@$;K$uJ1d;oO|L=V^Ky!rJ z9nc(^g!3ity&-Sk)KR_mbbGo}kYIhxBcFJ0P-hM*#(c~(HRinmPf0O{5UA2tI8+zW z)*4D>VR6jZvh5U0)M6tx%6~z+Xm&?2{;%D3&Yua&=Pq3YFw$&-DdAyMl$tT(Q z2}VjKNlAPA8>uAeQ<`X`-pMbpmPtnHo20u7?*JnWNZ!d>4m486cz!s}#UXON!;%?} zW{zTs7b%v!$4Vo`5;|<~cA{7^A1$wUy%WvIhKeNzv8Izv7Xoo`;Dw4M?2GrI6ifJ) zobc+C2eM?SSi+|OZ@#txa_u_E{+HNrq*&4q;v7#DOT4&ZiFb#Vqhg77rx6lK(claf zOOE6?<|vkUp<)RcKJQ*ru5XemI9{k&@?{>4P_g8F%mOb`EMfmb#S)sHdpU|FUXEgk zm!nwX{lK(0IXRx~g^DFNu%9`KC0?jlazER9+?1P<L2XP^MhtxiLA!+Ls%7BYenoXd44z( z8J)m;Dt!kwyonww!Sy5kS&mYqSRyYpR!pRS=EhP}>HS&uU@cpcp2#{P#gaj+>QGJ1 zrl&A-q*!t`4}YbmPD-D`)JU=97aZnDvE(wgJ6$tZrrX$1q*!txtBMp$hO+E&nz<@{ zEz3rVB^^wS6iaw!dNIY4|6tWI#gb**iI`$ZJ!^|8mQ1I-yX3*y-*SOMRvsyqP)E{>6ie=B=2Jq?53g_lPSw$#mi`q}BgK-*EE_47j9|@? zVu^f)i4;rz!cmA6OKMqFOtA#o)Q%TZERmP-T4`B4KhQCf7gH?hV0AIYl3SS`Q!Hs^ zdQ7o|A3wbvD3$~pkzrRAOFSr+$XAR=u|z(m>_oAIj|DlNA23z>@m$4W62+2RI4HZP zSi(t$OGNjc9|CGgx%qo-z~lTTv@_g&4r_!n?q_;@o%9g&#eH7IAiWnRtow5n6X_S2 z`U@3PX)YHA$2Dx&D=kSM&&)5V*gHLt;)^QwP47YRB^3vxpT^O4Usf@bUdXC`rDB=0 z*H-kAYDy(;N06S52Hf8Sd^A2U{zWK~Q;B$DSL_GezmXebcEyx0ia7Hnb1M^C19NtOX69+Zdpp{or?1DRM zFl{8=-=+?ybkgA0S>x+Qnl<>V+|M^sQ<-JX;Hk{=rb?M6ru{DU3L>2$2jc0|{W@bl zQ{?jLk&mCY;RA}K;Zq0UJF4RzP{cc}d*Vr@uKhOWCO4PAg99bP0oI(}%&xsH2; z5oQf-oVUVpk1XPGK^NX8Of#kC48Dc^I?CicdGHU}&RkBIq79)h#i~JXUK5b12l> z!M1AcU<*97(e}YWVJagA4|uQkbE#f)c6s^C?EHuQ!g7E4bo_^|&P;#BbUzJso#JwT zUW4Czy1%%5jNfB=g}=Mb1S4SQNiCm@%3 zm-?3k7w27+zqVja!hEZjTvuZblFq9(16sXwN#i0A3p}aCUjnxOAA4T{UsZLjf6hHO zAr}afQbj~XM2rH8V+bK36i8x11Y3(JC^&1iYHih6YsI0heYUnL zwNAC#I&`pgc&!b_SXTYK%ZPcE52u=YL9@0WaM?P2ZVjQ6Z>uf?rB zf{^{C$NVVaC(B8Q;YV~y^#O^aY+3y(udcrGT;)`ReX6Z1$#ne3ItC?o*d6m4+9Y zy|mjpJmIBBt-@srJ7(*#+be6TYCXf$GL$d6Z!wYJy9gy=rvxV<7F^pCu)~lNYjMe& z8^>NEJn>@X*xC;BS+ud1!@`_DwppDp=niRWnD#W11@M>re#%)HSl**#q&`}Wjd4w9A@QM z)yPetQJOKsYu7VMczhkriG#-*lJreZUU<|#>Zn80qj4u5lb$@GWI{=LG{Q+#mN!(6 zNsm6Nc6sH(+Q#~d%KG!tN1l8_dQv7c<($dmCyw!sq_tQK+;Fdk-5g|3TQui{)i9SN{?k5}U8Etqi%hR5>5oxeBczhpVIlSn3 zqg0kx8th+Tcd*ZmWx0*TAIsb9j>=_F#@4~E=zBSmV0UL{m8-z3sJun65;j{do8RKlmjcOug4X7g&15bfVc=(GLnjNimXp=qlOX!UK%(l-wJ zFg4OYm|w&GS$-3bAGejz_crF)Zq&!W+ZAp4N#GB)$6LAw1#qs^*>abnTz7a&ccI+F zwi3{egD5ZDn)ecJ1wTR}C@J98m~n7ioI87tV%Pf;90`4wJGxnYR<;;T|1fw8HWWIZw&E(Gw2Nq^X_P+3&LN1uyIIr}_#EpF-k7P)SZx6P|^V4pu|5YmhDMxZQ zqVTo)C|?UWhUvf(#lsYjQ9MC$kz%!CgW}bSJg(Gpr{V*OPbfaG_?lv?;!Z{3YoUHW z&I7PWu~<>QA|qY+T0r4z0fnyx6uuVlPA&g{;^T@hDhgi<<#wtppBNzvUkf+@bHw(A zuLTra22l7~z@?fmd@abr*8&P(3;3Ys3ttPe@U?)#*8&P(3n*VYfx_1U3SSE-d@Z2x zwSdCc0t#OXD10rT@U?)#*8&P(3n+XopzyVT!|{OQc!aM76uuTv_*y{WYXN!Au$+7% z2BuXOz7}NRYXQZ)0TjL#Q21Iv;cEefuLTso7Et(FK;dfvzo&YIuLZe9W#MZ<7QPlx z_*y{WYXOC?1r)v(Q21Iv;cEefuLTso7Et(FK;dfvg|7t^z7|mUT0r4z0fnyx6uuTv z_*y{WYXOC?1r)v(Q21Iv;cEefuLTso7Et(FK;dfvg|7t^z7|mUT0r4z0fnyx6uuTv z_*y{WYXOC?1r)v(@NamT;PHy%K}qbVI9yTO`67Lm$|owGt++<A@gkF#L1cI zQ!|-KGt%iP{HOmXW-?Q!mZa0ur%b|sZe8=v-sj5?4kh~n8v;1o{k)w=gJ-prerUiu z+X#Oy;GG4t3{Q!^TF_O*O1Ytx{$on9p94QG@UsKHjfs~+e=lBL(~c97w>|UHj$r!z zJJRq+vpw^aKM99#S0R6#qO&8@^U7 zw_gk19S4oepr4qJ@54tgFL1mKqj{deH3{P`th2U zVg9|?XB*uA4fl0!Df2HEd9NegYr3R$d+AeBE(dAZTsb+M__q`s!Zh1!zK-*_!(YmI zZ`zTMI&$37O*@u??+4w#Wu5)pC_E=WJpVZnhdGuTl{Cz$2si48yqSEZdX~389wX&9 z{T;UzZnN>{uf;#hZ{m-a9)`_yAg9XeSaV_g# zju(RXsL|NXiwAr=2>QJW0p2d~FSqv*=q2Bc7|zCL?jcNpjW8eIkOL(y#gm_bLMtVb zTohrYyd+=2tR$qSVBFE7gWhtQ(NOIN+GG~i`3#sO=D280?kM`n>1Pq z<}e8pfCmYrqbUUpXVae*kL55R#*(+NZ}W_}8NowlpxkrFlrbWl%|^(t<4?lb$jqL? zBr2BJKq0BAg^EtQMDh|z_9Zx-6Qtp6s5ikgw&?OkBDT$*%&Qzl6ZP~ z!Lz2`{N#4l3rS6j+4}RN@m|1iHgxh5{P=Mh!xAq(4i(`oXBxEntlp2K>qpV@ls=CA z_%Bh6q$b7<>ASpbVT!i!C$Qh2H4uja|NC+V{O8f6(JOmw2KnSa_2rznc9Hs7BqTV- z@VX6h>D9v3sW zpjoMV*sdcr1r(3h+_@?Gm_az32c_t zctuJ$n}<1ub7Q>NB%IBsIEeFN&!D)3vpJ92HpKX=Bw#q3vngK?dr9&ooDF9?=U5IW zkVr%W8c~*`mT)$514#Hx!_}EOgY7s{)4{CP%Qv&nFduBEPkaLY57>@>qtQDFXLCCX zj_{dAHUB|1BUS8?;%SC(HqS{e!r5?#e{$D|(Zvj^V;4$KmZZwr z?XzNhbj3!5;cSdpmpYKD$~1R<>Kv+C8tWxfi*PoL)KwlEBr?L;9K!qvsp)5|ULn;b zoK1rAa+MLz<|)dRDkGfDlRVT_DkGfDPbjZY8HE!Z1TRLJwu8KQ3krbL#14eX2xoIh zR7N+58lf zcn||zA)F1{^b%jf|F9ymVuGjT(c{4|`^(-g}O1 zI%t1Scs2G{uXU2MvKD@CaEvX*2FD_#*kC^-*5Ih}%PR^a7570@!NkAp`Bk6Dd@(5a z`SHgEeI^9G9uCe0i>BvM7eDExWxaVu7gkHy>P%B0x~-FWWI$uA@qW^1q| zeo4;7JvQgg%^%J00^FF-2ictbysP|6Vi$89WAG;<@t((-NPZP9=lm=HUJO5^sDxtp zSQ{3`Wa!3|)o$J8$g~qjr`8ZXcAo&#!I{xDgJNYZ-FC0Z>GeeOx8LmxU|ZdES#)?wO&NuiFb%FZoc-3Sjq$Ru3{?2vWBc&VzZ zZD6_f7$s}KgfL~vB(XOML!rRwfoo`0+3IpqIQE1*@U#$I#v{#cHm?>5 z(f;uZ7W+kg{HSR4ZNa{+gI({>(3j?pZZ>ZddVKU)km8w>+tw-OQFz@ z?I0mDkAJr-0%Us&if9k98_RIJ2Y)!Ebt2&3P<-X@4i{u5%JB!a-K;)h^wg||-J72h zX%6X(3ql_@?2Fy3J}X;{rvIzBAm0u5RUQ;c( z7C(nsK6p?_0to```%;6xH#8W(i1uB;*dxjBn29lv_rbX#f8;43{qqdOa06;Uf4<^36rWIhUGaB{e^pH2dBJ)I zDl*PFW%?r~a{U_dZN>K#d%z&f^pxUA#mS1Z6;D!Js#vSILGen(FDTxt_^{%$imxb= z>&SNBS4`jpraVxwSn&wOxr(PMRw+s-an!q75#_H{yh+o={}1Ym-yh&AULPojTbvie%MWf{BJ){8 znfE{DV;b9!ZPZXvUQ-p)Fw$M|Fs4l@DIp7^J3Nd(;r@%Q4G$yV8~>Yl^LTI*V84G1 z_Iq#v;^|x9*QRMlB4fA~@#b6JBp+cYxCT^^r*-HvD8aI}9Llh4Ld)*A^Y24DKToWo z>bFGA`Lynmx9y(-h1PzbNQ$*q?f!Ym(Wq|ox^LUR4Yg?7--bRr z+y4D*XL6AlNAhLn@s|-sl}J9s?uE8}+Gm|@e?FyYrey!*rPLDI_W#Pf878kV$;c>K zw*9xzfY|o&j?}SjpHI7pZ9nZp?83Hx7g`qEzRYZB+n>hnooYH1Pp+n-(6-OM?4E5u z4sEpU|ClvH+kO?bX503!W5dq2&!ywLVcS25Dnr{oT}^ak+y6AX@)a|{fyqBn3T^v8 z=g|ml`$tm>ZTp+pztFb-1l!vK+x`#PUTE9Dmi_F&wol%Tv+dI!=4|_KQwnYSCG4lO z?eB!x4cq<~q5q?0+dmsU65IagIdo^+XXu;IdLK`fMa4vF9p}PY{_|3kSl8M158-?} z+y0@f>umcoS=ib3zs8Alw*C3cb+-K?<~rN{c(&_o`}P zZC{8Y&bH4hGiuxaI8{e%`x`ijh;9Gx%#YaicTnEdwx8s1BDVb%P$RZ|xdAxaK7ScR zZ2NR=(3x$2I9e0i{y(VS+4c`*J!jibv(uf}_Wz7lqqhC|Ebnaly(l`{zWC5`w*7mU z>umd5I09$ef0DV*w%@{BXWJ*Sy<^)xf7V58`)jExV%tw}kP+Mdc;-iJ`%BnX#J100 zi&5MDvCNOy_K#tH#J0bfa%Z;vDiqiq+y0I0u(R#o!1njVw$E$T+V*ecp!bn&KiG;6 z=Z^g^yzS@3?$I^0tG|T&?!^%-^OaMKw;J;6u-=R)^itvvVoz%zIEXyO9A(nY4 zMa3D?z)7Zo8K!}RHZVKVz){jbx;L~s)4nVV@m3;jziGd6JY7i_%L3hEOEd7Q%nzv# zWZ8#lv%G#qRw$f<{h;T(SA+bEf}T|?V+okz`Hj0HpA|dGnE8N)akHpUygjsU4c~ocMp@a4n#Kwk zm-Wn|gMz&qoo3(1r0LL-u2<*YFr|n77(Nh{#oqdiiT|Tuh<7d<_M718<6(K_vc?r< zb&bnv%GXpjcoSS&`G*&8Ci=Y|LCL42-E6sR8+-z`qYMVgarD3+$BQ>M?_~Vh zI@tBzNqXLukp#P&ZG)c<+Y;+jAJOVthJ9NHyWV=(^h>#;o1N!u8~iNjOQQ_+^}?Uk zw~GxP`Z%X{vu)KPAu-97#yM4I(+|Qo>+aa#r=i@#wi3|lBbs+9leru=!%`&D27dtl z*j0{;b7#+G?0Vn8Nq;DJbhG-bY%!YtVc6h#Zn(|&aQH(014qsLlCVIucN|~a+JUe$ z!otYsk#$=jGZU3LV{QvzW#`;+W1F-|V|v4lc_Tx~%yR}Z%ksU~*s$%x%D6+&XDLj| z=ek#*NZgm`>u(x53UMA$XAH-GuFAZ=yR_+Is5qr1*25S@_KUF}4pZd4hVlZ%vlOcp zH!Jd2I?M6LKXIF)*mNO3q4JB0uPL@F{$4SG!^Qdo6h|tGO&956(*=r67brGepxAVQ zV$%g~(|QjoKBXu&UF3^R7brGepxAVQV$%hRO&2IOU7*->fnw7IicJ?NHeI0Dbb(^i z1&U1o;33ly6!P;9zD zvFQTErVA9CE>LW`K(Xlp#ik1sn=Vjnx!2a6q_zkY`Q?P=>o;33ly6!P;9zD zvFQTErVA9CE>LW`K(Xlp#ik1sn=Vjnx!2a6q_zkY`Q?P=>o;33ly6!P;9zD zvFQTErVA9CE>LW`K(Xlp#ik1sn=Vjnx!2a6q_zkY`Q?P=>o;33ly6!P;9zD zvFQTErVA9CE>LW`K(Xlp#ik1sn=Vjnx!2a6q_zkY`Q?P=>q?lFz3f#6vamX z();5k&htiWy140&pbV_m^ahpJDRR99%b&~Z1LgYjI>hsz?>m&`egK)zDavwR!9R<< zzcC&D1}j%NlWmta+liTpQzmuOW~(0y+SqJ&o`|*cSpPl;_E%YNJqzot@5lQ2I;?5m z*wlh9VeR}in68C8j>bOMy!(0m_#qRkjQX|Uel=+HHC#)6T8rm32Sc!K9%Jg|d#AQs z1B9vl2ROTUc<{hzZW)ll+W5@Ht#e@4-Fc#}Nk{&Bh{sXTOg(3}U@d>kjVQnUv6psi zIQ+Sm9K`Pk4sF^o0)Jey4*i4tWMK>DbkN4hjCpd}jx@^04!fcSYxZ~8_2?WI$Mgis zagBQt_UK%!ALm;4gJ6I5y}Fi1P*2wEW4(S3_U*d-ag*Dw%jdWY($~Yb{UpZOB<*-u zXCL=AxBd`o(YenYzk3)K8s+)6`Ry0#zkh!XPuDAJZTr%N&;Wpdr zeZ-%!egfD>*#BEH1)DKv&xLcg5p!gZPaXC-N1U_hJYB_e0rQk>+SL4LqX`b=a_cwWusSa4i(5PRnc#G+xH*wV`8 zoXjR1uO{i^aMa=aO`%R)heLrJXZH1gYaYkH$sd4zvHYD{&d-_)mK&*M&K8}RX>9xb zH!!*kRv+hhg#HH$q!5iJ-$kPtK!*)Melp3VeaP!TFdMe2lnydHB4UK$A+c?GX%`Rh zJ+CMUzD-~3WJj}U8}r76(HYq`Zq6ueS%cslyi4qx^>`HG(Sj|GRy=QGoc}#UhKuyUA9)u=z#lmtH7tK*I8<5w2m;>R0WmPXA;i4d0e{5Qir<13EPn(6 zZ%&3@7x3nEO2ZeSN#T$D7s>|4H^c%QxM0+1ze;LXuY8quk5h#}CyfH%KlpC=Vb&%NYLY$xQ8 z6j4pcAGsAHaRG1s$UaOp^$L=&QJQ9?{>f@;3Hc+>@i2w_kxFW5>l-42W+DM^qP`(w z&NsxfRO);~tYzQ2;E!+xs|37}nGN|PT*K`G-aJl4A%BE@iS3qe2(I#Hz?;FW8S+PP z^KKLH<}%0uqb|i9e}ums!hkp5=M2&%NWIrVz?(H}Amon}a30(EhKSuqP7k~VB)`GN zL;lF&tQYb}HuGqN{E?rsM#vvo#{Px;k*UmU!yk!#$FyL4L&VznhHwFIQfx2ekDSVW zw&9P&o;F(Mn7~NS7Rk9cKY1PNg}x!qWa}Y+pK3(-OP3T5nh=Hc(V~8 zz9H69b%Z}MhZ7Ltj~qj75&p%=!i zF?uHfZ*J#wIsV8KRO9#~KVfbs{E=D~SKkl}coT4tjz98ER(Jdn`sqNxn|DbE z7IyrRUvL5(e`FhT9e?Ch)^+@mBsI774G}wAyfMT^)TKy#L%-X`BNtIu zgg+v2c_RFg``D8Rf8+_Oi};4%sx}0?q55D1d_#Pi`4Rre6_h*UkKBR+-Q|xk;LSVi zu;Y)!Def+RgaL1Ot>Q3=Z-}!vDf@~)!k2@bpPhjJaUYWf_DFx6kUiH-MoRDiTJE`p zE=GcUrru3=K*2K@RL`$UftB37UkW^p+C9H6yH~N=)6^NesfLo(S&`hGx7b%t`2P82>Le!3kC$DGT(4!W+3hLyaGIuGGkVj~77Kg*J1 zO(YZz4ww_c&f+ zId+&OzBznva1mZI{go)=pYInTog3FqS_aKJVbBXAF4l)f+R(33*g)q({^>&>X0Z!ISTd}}^EZ+cR%+fPDxA9pR@dzpt*?d%huv=c&+D8lz#dPNdj7ciX+x=2)}0s8hy!Q|M{})pEZ)i zpL2Za_u^5~?{g&ZqkqaM={WLOaiMqcsc1VEy7E|s#$uklU4CR-c@#IC*U*tOkUen5 zv1bFLhl{eSC1Xo3;lTDJj1?t{lM|D^|0HpwtTH|1tQT7%r5Q> zsEn)lZ=Q{%bMcf*zr0}FIJoWz4!Lz)aECuH_~wSHU@F{pFgi}=mHA)^1@C+F5}#c0 z=vV~F8Cx9OwqYJzh0F^MNUUYhoTxyIL5{)n2;DT zv3T74;zUtqT<|I?{@$zlPH^`HRmY<|im}4{@xicp!3sJLIS^5NP-4Yvh^&LGM>6B) zPYqf(%-d8M{MwrbKO*Diy;3}W(pa*Wo<}lwU-POqjV}(~@otSe7s=){jpV>}Z|2XQ!#fi;5`R$Y&%VdC2|01Ix%LIDo;}YSO4{HvN8eX$M zh6LGXUHRr|3POu+_SYuGPFPLdjK8x743>WZ8H#72PD{rjb{f`}83zm>Jit!G&IYXw zbqJC(8OLaK`3eMw*(+gp;FKJcD(FPqofRvW)1yS^{zKMPSJqTi;(9Dw#_PAit1dsc z622(BvKq^wlJGsYzmvof#olr;(kNXtAz=2{(a2hG2pNz_XjSMZlxi#;Oa*43eU23` zjH((8vrDGba)gjt>#be6rjpZ%CAXEr-rKYLsoV~WjYWfzR`mKT>%g0do%V@5LN$n( zv&v0SljS@r(Lg(pSJreIRR<*)g=Xa^xnkMO4MI8LN|51|HAoT)RdB=%^VFRP zI~{Vjc9XP)cFRq>?KwgT*;|HqCG){!Vq#}SJ)u}$Vb=~(mN%|hhTv9>^$1vnL1P+L z)YhMea9Uji(m_vgV#wvi@II@jmXwzd?sKB*LP4#SocOQ%r;urUL#(>MT+>9Ov9cA_h)H(Tt3FT`MWno$4TJXNQzqRK>CHhFT56W!*)=hYyT#sh@ z;}36?9?RiPcNZTj->?dh$BkvVjm00!<4sAnj>_>_Z|kri%e@y#u)DLf%H4|}Q+yk+ zn=N-Z67Vt|ZUw{P8vs8?I1W?^$C1t7`WW~i{B3VHt8W1kqWY>oX2wN*9G{(+W!SfM zu2zF zN?T<>t8Y`5zMG(rhBfvNChxF+H(;N2c>K6cfxd?@&vs)T|87^b>3_gSRocbv#xmUQ z!5_|*IuY>iV4RRctpK#;$Rx7o6tm}`+{4__&6XoZ4yRX#3*j*&lD~yRE#u(0cr2p& zGDV)Z0%>-$`mAg*n*L$%x1#3^XM@Lfu05$BGoSCyW;6NcM`W8r+!uVVKH32LKz^2e zpJy`XnfD>PQO0Q%Zj||X&Tf>W=LqGaY$lL0zlzw6@?t9hDVO8V{+3X_nE9PIvfLYq z_bB^<5;`iu>kocUSpOx+$D4ymSW~_VtXs0q;kE=m+E#Oq?>BfgSyR_oUny}a^;gI% zXqWy<(l|ke;G&req`#8oMEECJgp;xm>FbFocLfpY+f?SsDE>@gS@o%_Z zErx4oJ&^uLc#J9Y-qs~|4F_om%K>>WA(ki}rZ`)X_dVvHrC6=FS@Ckkn-#YyeoOHQ z#TOM{Q*2e_cMj?o?iz3a=7;h~Md7YN-tUjZ%!^n{FdxEQ0}6KyxZfX%nVb2!RsW38$PkO;y^{=t|5Jb$}<$_C}zjIsnYbdiWezfqj;<0eTok&ic<)*^K+Ho zRQ#hNUuM|9e8ob=Llmbg9;5gP#YQ6Th36@LPSd}v_<-VLiq9+lQ4tIqX}1U74XG!o z*hg_Z5&fE>^2ZfV()2RL8bw(!2|c(KxADs|Q{`Diy=TD*dOjH7q<~P^} zk%o~hK%`m1oS!o%x5ozVgGj?bZb5oJq}&$d1Vzm75?A5hXYtpQI|#C3YQ=wu(jd}2 z&LSYvEQMZ2q^YJfybO6lq{*c?;tQ;Q3EoyhBFzZCy7YS!>G8ivUwY32d>wxz()Ai$o=vTEyC*ZqO6IBhs8pEg_NSH_Qu(G`CQTBhr*0 zb10e-BF$)Oo5Lg;^dNY#qbW&{l@u!;%V9{66|Vn}%`+l{tjs~Vmys!BM39xsAzzI@ zA=1dqp28$59?R~XYPudz-bzKMT`PHsB>NIOofD)%R;V{YfA^$asd5HcVN&m(;NQFW zBax;jYgRFZZQ%X14UvW?RIrf^*YYIoU4+r3u@e$$v|=Yjnv>YTJ*>zeE2k3gHIfOk z61z{>3OE|ygknde8OM6x;M_3C%J0}M1Z_%*HAS-q3-$OTxG&=cP)_eGV zkxY=4*mq0|mPljzY=W#fB2B=0Pdq5~W*M@~lMDh88)1U1#GW=<<|JQbdm)i#9qav| zNcvft{40C*{A4^_7-Z!vN75Z^>{V4XM2=gL;Jj7pueiCU$ zL+D%EwlGCon2}TZvqqozV*KyR8IVXFM(-Q|d5{NX@ zS=SM1GAukh#^Ex^3cXZBh%~d9dra&RsXH?@kGaRi%q?hEs*>$GBF&?0=y=VYo06kA zKlZrPouB$|7Is9MT8gDH9uo#xSwhYAu}@3kB`IE+Aky%P6e3L{RiBHFQGPC#lHYje z#eRz75@e->+BU?F#uNq&vT_jR3u3>Ld-HWAT&_la|C-nnapy%snl} zTRDTQJVbOvn#Y-YMl4^Zw=|VxOJ~Neke)0_9mH;*6?<{I{-LaIxUl_%NCa+MKeg>J?`q@j8t((oY* zB8`y|WaUcAD^z|8K~^{jUW_tr2(of33V=w%4ur`FvN9zqBgo2Gkav7r= zeTMDV#ny<3AS>MAjZ4%c$O=zM9s-pI@;cf{B8{xEs*b&mNj&JY9P~Lcw&^9lhW}wt z5M+f{kKd1%H%=O6B32U`9mKOY46^d7q<{Ya-bAGX-Gc0=pQ(lx=tQK-^u=gMHCjIJLH}WrXNPX zFO?cEE(4Lfh$V~{ml2|8vC%U^^qgY!j8Hv8g`Ybtkj8!QQ=C}uEdJk5w5X|Ew|eEW zV*FJ^9joAzS?vd7T7*oB!T#|GkW!VIyLpqiQjzbWQC}+jy=FHa+d27hxJ$Xp=RP+g zf5U}5gC+R|{3eN01>0RbJMd>d6V~E8;3<&IZ#gMYi2tT2PBh(k`q-^Yrh(ccVbF$^ z7glLnyh^4cPQsc>Sbb|M&h!2~s0JPL3(EE?uVAmT34}%fbLI*dI6J2iIJQ8C9#@RV z>avP+*TSF-vQW*6tk@#;IIc=ZD65IY?bu^tz}^WvVn_te?%$EAKJ2F0<-2y5vb=n4 zc{>((y?8!kyZZ+wGw_DhZt#*Uo4vbJd?2g+9vJL-2Dh=)^TaNzYOEm*gWM5e5{LoA z*#2>F5T0da!*aT6LV@zt!T~WR4&= z*}DmY;Hx3W!S_Lqm;K7Nz2Aqg8+|__O-5ZSRjj-*a}5C`Wz#yIm0=+ZNahr&|GN+r0<- zIH&4t)^|~Es1+jRvTg6bM7f9CDM!8L<;Y|v4)nZPNK8TGc-U1U$7;`I?0R!?zD(tg zZnl3`wir$SFl_HUH{51?r<8^X(cW-8ZAUn#sN(3Z{5X?Ej0j=&%D_&&@0Lx6T; zzBNL)a38c@^R3pI!6l`QLED_mg*fQ5pn}LT(_ad&?Gqz!JqH6pv9n zLGd)jYDHd$tar5{&wI*uDn6k2gyQpxVrxb@u{8sCYWn+%{cy;rr%16_QNHjXT^8v9 z)~m+@N@s;;o8bReV(O1;y7D|Df0h4=1*NgyI6lEsEDEime&t#MTUy*cZUQxM9<# zIY4nL5$Up&2uR1Ylvil}D#Z<&e!1f36hFc10{ZxTiM|OVcW&F3f~zmw3&M8&$QB3in}7rWlbpE6h!TB-Jhaxbj@|J2n6THeCjcfsfDw$sDncywt1RZ!LFty@OqjbbG!55&{ z8C1VSX*hc)2Gze)v++!@7Pr>Wpn3rBv&J(4SD>k9f^T4#2GyM$Py3z;W-)7Qk?2n( zzl;i2%99m(R_eZIf|sC!2GthooMbe6#>5pGRQs}>(4cxJJ0BWUw_=FSGr>Aa(~Oq> z$^T$)ryFTt@=fN22Gz@{#Tit`Ad^q|u00b3tmr%woWZ_z;hBI|QZcB?%!Zx`irBr- zpn5wMg`Nr6m)-MB@CP(PgK8dYcJ7&gRKbX60-D`JgKF2F2^O(|&@;i&oQ@7W6MUP; z^eaVjwhTvQ!e5AZCRooS8}Ur=2@WFSnP3{V zMLZJ}Qr^`w!JpYm#52J{s1bvz+yIrlQCfLpia0b<{G1qw}$Y)*WnV=6f zcj%emXPkTInP4eZEsepS93EjXs7|J?h-ZR>m>)5yUdNt9JQF-kbrH`57coELncza^ zM?4c;L%B201h=EW?sz7+kR5iO34X}-_rx;+uT|@rKpcteBhLi!zhZzesIEf4joX01 z7DYbX8V=CSI@L)Lr#0@n0PVNZPr z$FfhH2Gjmr7eAP<`TpA&o_~g@r)s{tK)q;OA{rMOjZ2J1Z|Gc3WVmi!?Bw3rOxtfx z?Z})eKYl*Y%JtBm`kdz%k1sxUAs zhV-jY-F)`}!ow_137Xycy@|cXr>Dh8Qb~$@V2f(DCDk>x->6z??2?Sr7hSXRZ*EjA zGp5FmkWDpQM7_6+sU0q4-b;4W4`?6NI}lb$*EaIpLZFV-jaXc5R!7myxK50TRlMer z-;Kr3_MXs=85L%G@v4CUGt04rt8DRn7~8!vERx5GP)X%`vfz2Y{j{n!8b|_*@hhWp zRXsvYL=!%==o5QtKdr`xM1D);bBWI(yi$r?Zj{P+HIZ$rq6L=a_F>ph`9!pvZ8zI~ zx)Fm&qw^dGzk%9uWZO?~gZ*@qRf2MMH`{*N)J^;8NPNSc1wD3SyWFzvr`x(|KV1%e zRKYb?BKfZg|@ovGstwROZLSLFYy4k! z5@o56f43_FWLwou`{^Csw4XlSP5bHZus(DaI@Fc@bR5o?8Md=Pj-kCRn*L$fPjPxi zHeZfKX_yl24Sp2v1N$lM*4+4hO8Y2Ii*V!l!TW-JcV&L`TxGs}PX!gotsd6b*r)}S zNp0S*P?hy*JDm?40-+SRH*Bc#SqA6rZ0ZB@S|`eSU0}7!n-wotyjgLZ;8NvsK=e8K|G(RATK zLtd(~#F~J7zRD770zg_fS;VVAjT*nWa;2)7^oyX6_dsv$9 z(nZdpye>@L4wEX5B(^xtRr6#c^2y|j^J9K)i01_VKq)nz9*dGNp1y@5FrL!gr!}5Z zRm6Bo`=1(5=_tJ;EBR`#?x0YP0o0F2)hs(Py17I zXgpocyK-ne{UQ4AjHeG%3XP}o{0WVxOW60&c={GwXlpzjgJ!xmo-SZTXFM%u-?}iK z(kLRvQ<>S&czQQG6dF&zN=2dZlzrJf?2gcK{@R)|iQ!zY+#?t~0J~WxrGxL8cz>p>!Dls>nJ(n=>m4NYvXAl+Wu%6PiZa| z@ziZmF=3V-I^!vs8WH1ZhV$o)r?Rxz8Bf2;!p?a53r?gno<7Q4XFT1=T<6xkkw?TC zPakAM&UpGK7Iwzd!4#eGRE#UmczP9co$*x6l@a6V8mf*MPrt_rh!{_Kn~WMy5QjGQgp`C29|clQ}NdCjHl0XLY(omhWBP?Je|V2&UktwyWOGj^aq@KXFPp`-G~@Z zS5Q~Pc)FbV5#wnSdlE68K0m2mHGM@ek1BCJP_n7E}@#TIV4rjvna{o0`jW74xxNCg5FW|26<^BN1 zmoUEE`>@*6CqP{=868fDFZbSu;~i=T(q{IUk^Bmha|d(PAr|T;kHvYBTWH**BeonA z$7dmjC z#=MCfu{dyFfSt)0de2RqVsG$$ko|MeC;w2t2ua-dvBVmsr-@M-4&0Y}zP!Kq3y07@ zg+D`To;Cr+4HQdeK8Vw+X6A2rwL@*8sZEzI3^h(5zVVeEuS#~r`2;`msPc5Tl5 z?O`{b4zgbN;@IY3etyAV$T*_d=naY|6u!n8w|z|HXFijSvr0Fruv?dFbh9nW|88q@ z#SM7J3v}st7cO|=5?Gf0Eh}%J=VaLIs%snTy*jB4Pls@PSX)z8xqc;F7|T-SirU&$ zD_OK*1&w{u0DtO1UuUkr<a}HXo`@B*(y;Yu3~$Z#Yirk4G{7PUdmddh z*GGIbciZ(h`k_9*-Rj+a;p-dih)Zr`f8HxDzoRRA&H7wpG3C^XW9xPGD>*YfO6OI= zO}1D`>6()3Hfyn*854&i#e=haC6CX3J&U#Y-`cW^|t4#$g@^ z$B}Ipe+wTkueVB2&hBR0#WNrpa2}|S2-SkifVK{Hz4Kv2rk&7kEXOU|EbxZO})KgWYUiwq1N~q<PTW>o)VT-_~vBVZW{0%)`IR)@|1di*IUNr{bF$D88wIBlv9#D88wI;+q;c zSJSzKfca-Du2%e{;#Ngjwuy3IQu#j=A5(lm@fV7}ReWEu2X3Wor@!KG#f+i^8AN^= zWjt^y6gO%9t%~A{8s+4BI?C~RAGOuXeGmUE@|}SCs@7JnUhPctUE1cSO)1HwW$W4} z^!x3sTc&LuZ-{2yGVJpHEZFApYBD`@O{<4k^dH0iXtlu)$!tPaa{ya>4j%u#;;`Ry zoik(G`-A$uQR-cv!Yv)?{aP@cgAzcz-}LejlOCR3S_(4Pr(&53n6fq&L~Z;T=x5w| zwl@=Hz!Yl6%ohAP5_5j^W7oI#X>Qt42!GY&3H*UE=h1Ep%CHYV1Lko}^<&qydf*o1 znZErN)4G2hbq=^h3^i{9%?~^$U7!pU8z5#BYJ z{E)8JQJ=#Li$_7J0B_1Z4mA?}N}L{c9rb@8L8$;qylsIB@NE4 z2o>NG?2-!bW#+a|1rU;0xQ==~DmW^DtdrHe?o$D{7MoOnuVFln3Lr+KkP7fR^Fk`X z8JzBr3c&CpjtU@-MnWn;9S0Ip0mOMoNCgmsS6eE;?>VJ@>?q^Sc@CFW(@=p_FB0MX zAu4rLfRAwwx}XAl7Mg?#ATt|M0mOSrNCjx1qL2!}zU&?q;E!m8RDf4lGo%8DrWB(JgO6KBeGJv|1N;c%E0Nbd_Q30N2VMhfxoT8%wjALO(1>lt# zjqpB&sv}f@H0L8i1rTTFk#*E>v;3}9fWJ^}gbL6TYJ>_PHvmTkc!!;HQ~-J&?`$3Q zaI_}tsGB%ljtVf1^&AyIn7N%$0shEVBUFGtP@AIy+(*$-0S=&YM+KPA8F5sAtEkIS z0q$n5qXIND*HHobQFDh>fZI8jjtVesMKmHI`9VN#R99WEu4*B!0MEJ3W{jV4oj3{X0bu zb-ZcxFz)vqQnl0@+Mjb2E|LBMCGSlB-#hke3E}#`%6=`svv%KnEfayz-8h@V1=4SO z{^T(ePP}A(22s0vPB3A#gXFwRR|V%(N%-xcZr*Ldnu~(z^DgRO{WLyTh8vGDY}s@3 z3$F5M&c^GwY)oglQTW4|hOH52hgmz#;b7(T;Yg65kdob=m76Dz-8!*ycWmEQGh#;D zDO*fmcrB-!f^BNl&_a2)*Z z%JI^sdouoP9qf9&@W5?~B-mZrmZJJf;Ka5&w&DAF8~?l1&CSm9+1QWjn+bhsq_Kba z(iZkF+cx|*^o>Ej-E9A|ZNq0q`p0(&JKk*D@B#Rko3_e;R^KM;d zU$$*{A~MO0w;T2G?{-C--inXK)2$H6y9fI?r|N9h5PY{DYK2I-Y}@c*DAyg^a5a2R zlbhO=ZTQ~xbrlZG%Gs|P!9L^7C+HL#7Hrz+m^L>=IVZM{vjps*{w?G*-1o`d5 za_z67)|YW>I@Xt28-E$EOC+nA_uAv@SyfLAjcX& z|IQ-0Mfe{S<6q1K>3s~vU<>7%KG;If#Cwqq()%Rp+918L;ki>;#{}tJO069P={2?D zN27=h(iS+i4^yO2hnKE1nDiJ=24@OlNS$Y-wRBT-kVVbKBecO zmyct2U@#_m1TcsO=?&Dv8BadTtk4!pBe0e7l1E4K#1 zCfRXJN5vqtg&xMd&=y+Fc0ya|DE48hsaGIDgoDr)x{6vtTWAhT&M+klliy^XvxSzR z37R4jesENhZkK{ENbe%nb3uAvXQm6%`x*A8iy*zUFkRyZi_Of2L3(FWacB#zqTVn_ zFZ&YgR*>E^aZD2dg7mUOVUS+NaSc`!=?T#%{#hRRU$ZF}r1wFTIWzcjep#It-1QSQ zj5CQrdV_6RFTZS^7u;=xMDh-*41@Ikgu2=U=?%gly))TZXbbh(u`oz4Sp-2Cr1xf) z41@I2eM%4p>7^&ypiPk8piPk8;8D|O6QnozuIcB@7Dw$LU>Zx9CQoxqae zvgmaz8QMZ$;rPNJy?0Y`L3%4tv>QQsxeV{46{Pn{jM$3@&!3~xMj z2y?BNNO7@WDCVW6v9Pm+ws78Dklu|f>};X*=^eNry(o)IEM|_?^c3xHfeX?*goRy@ z-Zd=jY@th;>w@&sSQ5A(y?HQk(xgfoAjdv2H_bm*<2I&nDq?cyuzy<03ImhIJ^xjD2E=cdu%snj((mR-Soh_6> zo&y)8_XjNOg7osOC~!e~f6TfrNH5tbfeX^hZ&X1fNbd!dqd|JvuZS(Qp4uWodJCB! z3DWxz>qmn0UeD$tL3-a}{mz2)a$R(ba4B8t4Y{1t5(eBo57PdCyB#kj4u7_Y{j*FQr^2R*Gt;il#V*4`nnZky zUtU>-eH@4O?4Jku^NZIMR~8>{!tHkI8(I#a#@ppL7;a1(hd=m+E~U?CelJ1?ur=`T zt4H%QeH1g(f3eISWH* z){0k#inU8Ru~=+v?W(fX0hnE59jb$r>$M<2EL0Q&a)q2lYs%M4H`XkR@B{QtfXg+DbO0yQI7T*bwsc)OXO}=5C)DP8)y{K;^s?2f zjFo<64NicSHPErHBEmp8=D7JstVa>?-u;oI@tA&g1$6&bhCMzAVV7f z15VaHOho^r4{U$q4R8Jp?I+OxUInO|HtT7ny-an1f6b=8{xzFp{{2lYiGmwj`!{3E z=vP7d#@4uZ+K!%H(?rZoQGV%#&0zb}m-cmjrfE}-SIYgLvXAI9`gQh>PkP=DIJSJW z(b4!$#rSfrIj#A8^wo^BQpTAhZS+&wRNM$0Q0_X}8i8_Vn4k|@2g730JD z-^Dv{`W>-bQJ&|211$ z3o=`9UZCw+TUviYJ?J;jvw-uN#@wcxHgO!-$7BEG77T1q|57tHT#A=*Y@MAy&;IkD zKiHmmdB?2Grk3rQ?_&(jE%j)J$DPMS<^=6|n3o{4T>2IFF2cNTX?+OgScYw|9LLD{ zVt=FO|NWSsR`wn9$g<=B?5ex6IVXKlGmkZOd+9B$7hx`=dS2rBe1Fr9^P$V?=&J2n zwEZ;O#$0p$+0Q3L#yjx=enF4E_&+MYUoyZOb!>oV9f{-3Z6t(b@^JhZiDNfmc5}1{ zZ0WdBg=>~kC|B1kV-Yf5^L?yY=1$Kd@3?-&$nF&`c7~92WvAoR&FkYOk=mO@*e0Eu z;}su;)XzB!^D*K+3J9rkVkfMsoImb8xVS zm%xq|Vh?G%5StOKqn_HGPj=q!)4eqJ09=?wNq;f!_rtY_qbeMOlDN5IGj7|P@zTL< z3ljZ<_y#f@QqIr#5DB(JQ#?1$k2CR{7_C7)awju`CKI@J|1S2ySolv=17m?zdThYI zczk#+*BZyu3nAswR2FZas-P{1k9bQ}h>u9V0fq4)Y>8Bb-%<*x3bCyeg{lzy4jv8h zQHP>jUOYx?grO<~`~|I41sbJDRS?Ts=P$jpXR zg|DzfraZma5 z#YF0A&V|j*OKsy}I$Y}(r0}j2r7F-C zqbeNDcIRo~IjJkzkfSQl8GMAQAXK6gwQy7lv85zTL|Ys39CD*0=-v6s0zPguA?g4#tAuHXM1kS z!*e}CRd}Ddj;io^j?YmQ@ahnyDr}?Zs0v&G9z>`LL)ec9Re^r$gJsfkFrvv%)v>Jz zRpE0~7ojR}S$CAGz}sJxs({zuPN@n(B?{~gRpB9a*ijX}%J#dUD)11>y|$idi{wZFM_XJ{#ipvZkQnXrw(acSqotb3!gp|rfbg^ zf}_7^C<;yH?bKf~bY<r$-OwSxo2tqXj!)-<>ui6sll1Z#olIaB^e)oa3*rY%;{!-*HD~~%wZ1c(W9{95;d_0te9h|RE6J8I3<%4Vkps|X?#qN{v&YfH z-N~ok>@jIVCdwwc2$cW_xy%qVvYodxhxhtDV6^Om_4Htg@N^U|&<7&5gg3niYs#@R z4512V86SC$r4({OlokPcNQA-Vs2bUHiwK`Y$s>k<5fWGSUS#qQdyT%{Au90krOdI{ z9K$SRH5n$DGIoTeV~ACZCQ!aQC;XJ?gr z2)}{&^tGEUcQ_L8Kn}NGU>tSO$#LLiI~)gWBW>CZV1$}rp4n2@>RSLQs&D=XGcM}m z`0Tta!@jM9U9SXOiRs+Y%}&$VkfZt*K_Bg3)W_jieGS-;>RSzcMM$%o&8tO1RNwMQ z|M-4m^<{HXu7tj{RR*;BHeo-iZ*`=9_<|nlqix>S!LIie=rblZN-pn9y3gax?RzMf z#Ids*b@1a93%mq4QXZcrPsx&JWy!~8$tPyX;m?&YR(j-Z zsjOjLL*-hKKi5>Qsi>|etf@U$NRvW62KU?g10e4hV)W(HV_yn!J|{4(h5hg$f!H7S zO`K4LxS-=0Gv#rLvxqpcma0s{0cC#TW4Y}r^IJqA&VTXAi1U{BtuFo5m!%*An_JV0@jB27U|KUq<} z#X#owai*WI_*KOR6~zfE@?TY1T+~875XX!4MkyYtc%tH2imMdQQ{1BXT}9#FK#%Zm zfWp54=HMno{ldQi4p&+DH<15R;B@lnMW6kk{TgJK`NAaLAAC@xUkqIjL+PDSxo zkMce6uwlJ?#X?0cJYqT>?h)rJp02o7@d6^w(M^itZyx2})BIkz?K8g*u{W+uBJyV{ zuIKfGeB6@S@OUt-9>U^<{2kt}g}1Y=eD$i3wUh4BfBf``QzoTl>)L;Of)8TcN=pPx zZ1mT$P z?=^svWBkqcX>Qsv5S$sWZp#kVIdj(LH~#p~f1d6wC0FN^mUqBMAfJF7g`Gzle$CFK z(RQ8V3Ne2|&5dt(;0$5D+O&_U4co^l;o2`~+CiO|;$G~-AogM7*02x%9qGewwGRoD z7oN}2%JbQU+u%41L6qh|ZiCVs{?Bt8I-)gX`Av*8%k5L*fFA*c+Sh!5l1hU_X2TeM$E6fW$HqgxLyw^X6{yXpW zmr@EnHoQPB(~DSp#K7bwgC2 z;=NvGHuPSE|aV%Og5e~n@vE${U- zNPF?v^QmTzzEo%gsF+Bt=FHgKyi_ggI*$#9a8{i6`a@aQd2EmuZ0EiH zmz+rFvEfpxa^CCDX0G#IKb-A4j}05xkn>(Iul&w?Jx%Ws@Acy-I`8$Xso8n2=at!+ z_xcSSM8tdj|6|h;@AYp`-qm~kTn;DVy?zDMi1&KA0XXmVH0?*c*ME%FItgSy9Ic7> z`hT#X^IpG>YMl4_G&|ji_xh169`Rl;4(XludNN}o-s?|dY3IHE9_Bjl_2+YXoyUeB zG1qyo@5{Q*d;KzYyF>5wpW$3O@AYe`YH19tXgtC;`l+L-D-y_lJo6(S8%|(PBHrsa zQC-A){Y>UZyw@Mj{D}8@`32sY_xiI@VRyXOe~%q@-s|a_V2`}l^IEmu>q%4EXWr|- zg#p52!>7@2^T$mK?QgZ0>Og7IEI8q(oDeLUFm__`rj=kwlvf6m zswUP{O$?T3+Hp*qP&Kgwe;Lv5vOFK>7ql$jyvCFGoQ7?&DCkm!`!U_3}f{9EiLPJ=KT%EHt8 ze*wi|ukae$o`Czw4SO@xe7o@kqE8L`EXn3G?8I`DG@dH_rtRa8H3!h;+Gb5W7 zv-u1imCK;6twS{|_n(mjyE{9p99ijnYTC_~<1Yt1J;SX41LNm4j)O0gb{r>T-_~K_ zSMl-rmPmr#U4Xo(zMF=bd7wVdht;Jemg1$7;sE@<3 z`e<8>>U$3QCLqmjHm?>5QGJg^`qvqs;jhq_w#tB3-=-{m&p=-R(%3(kJi`9b*2y|N ze%uD35bbJqV;=u*SG4JWhN9_Kh~(XaeH_v{n>8Nx;GtHCl*{HR9FKAjw^NR7o3|yC zx&9#Z6^SVBVdydrj*D}LcEfEO&ii9R$<0e7Np`8m$qUbC|Dhb>y_IDl4xzr*UOHhyyOEz7me58j#fX zy_fwcM1%a5N?#jSsJv5UUejIL`k_Q@{lGCS4=hm>TR&vpUzpD8op_dFwc=*Q%N1`{ z+@{Fi=dAaH;){x}DZ)S|`M*~r2ZH4XD2`M-U2&OWog$aavHTT^H!E&ad{FTzMLO$c z`Bue0DE?hBiHn2zgA^rp0OVs;UaVN5C?8jme}~HVDsnvu^}L`c-wq*jfrjW;oT?}x zc9DOw%1ae%6wg=uh9cKsQP1m&@;MmtUsX=vp2K|k@((;*W%<|z`BjzQR(wyf2W{=h zPbtFSB(lT~K>BQzPf}c}SgW`}@k+%nDBi31u;R0duPDB)_`YHSCoualP_bC?2*tUI zrz%z{UZQxl;;o8bQG8zUmx{kr{F`Dg+T79ZL5ialrzuM80OT)LdAXv*rbqfFm9JC0 zL-7H{Cl!CH*rFK6LzC^6C?2JFzT#HJR>j{b{#`K_H$vLblZwNL(07>1M<^~(JX!Gu zBFcSL@h!#w*8D#!_QXS&LU4M;`m)dCCbuO!;eL@#Yw8^J~+$wfalp z)1lxqt%daIk#1^%x005;^p&licV%nhM_0l}1MKOuu52y9ABKV0^YjCd<+;Pwe>MD1ovh2F)$bf z;bY)Y6orlbIo`0NJ_d4z=Wc|)9OGl4H+MVmF<@EiW z(4v)Nd<=XOcl;ddW8hc_@qTZjOTh@Vs6GbxgJWQPO!8pnMtuy#)vG`}`5h>1ALCfLmva|Xgu^W@D}qz9|P~P|Dlh8 z`zVDz2FQquhdu^qLym_&2Dn-?-qy#!yF5gG{HSD_?SX4uR2m*Cp z9|L@~zQ&I)HZvRg7`T-k3VjSbLq(yF0rn-nTj3CvLt7$VnS7l!LmvZAP;0i2fp^)k z^D(d);+b&?hY*j3Lx^vi3|&}5N*@F9yN!@Y^7lhL^fB-$PDh(?2=Oo+!e#7A=wrZV zz0k)%G5Zku7`T(iE%Y(a!o1MOK!4`7@i7o@6AmHXCLBWiyQZHrlcZ$CLmvb5d>C)z zV;~-eL&#@d=wsmX>}Tj>;83<6`WU#JQWy>)C`CuR;bWjD^nbK`415nAhkujjQ!x(R z`53sGqV*yWPhA)l6RDqaW^8U=>T^6y&d0zss&wHH&SYKZW8e-Jc0L9Y6rGQO2PnF5 z2WGhl zdpU@RkAZ&F772&&2bSN}$3Q*RMtls=S3_PL;Sl83n(#OB^%KkaXR$ZV$G|*R>%_-E zFw9X>WcUnpr4$0 z#K*ul*prBlft#o<;$z?r=10OI9MAknIE2$FcM=XEScL-J^)V1fIE0<-u=6qSN4CGa zJ_c~A9>i-Ehe^UAJjFrpD<1>TVSw;4a3lI1I3EK?p?Ufk_yUrhj{%a~+xIa*x;lLf zTm)^-$G{Efxbrd46LYbPkAXMw`@sJjYWt`7MX1b;uU!A1y>EfDsw~%Empywn>=8#{ zD=OfIfdqw_5l~Rkkc-MqM=pw{4l~1WQJ9%#7!|uApkkS+shOF=$?}qQJSS7LEN`dG zv@APi9#0`SWo8}g_%|!d|MR@-{VsbmH%27u@cm}bv)=E%zI9*EdfpE{2IwO`JeB?p z!kS@xHV=iohyMUPqZs3KaYb;dRI}({aOv{hTDb6FN;Jd8l>SFoXAY)3G(3Y|4Wg?_ z)st|{^SwjLPRj5FSWzH;33tPs^eda78eO%js(xitRb?gF-m2vd4OMlmtH(7pH#LrH ztgByHHKB518t(wu@YXv3menHw zmOA$c@Sj0AF}|oWF0YJR^#76Y5m0$pb-cE6V%&fBC7TXAe0hBSGGU~-`F$-u*C%t}n~sb$sUzAM!ByL~45 zT_UsZ+g>O;Se4!VD2_L?VL+25?)YVz z{}JW+-LlW`E@8c%C3Gr{gh976FbG%62}qKu9FmDB3g$hrlYhu`Dz%H|Ha9H~Y71sD zF&+UL?CcW-vs@%oiD2)Si9eEgS9mNm1-n{bzeiWUbk|YOpbg0a$@@-TM)>a0zog(Y zD502lZo!2rj}*SLWO?Speaa5J^FxU91M@N*h9C3eIxOKKMFS>ZH6qM1(;mX_YA?bp zPD8v|#$v!PZg~YY!K59B2roLg3hS{Lo#%S+ov&GsV%~W+F64KbN|4Vil85r^dkF*< z*NgQLO?~xPHf69Fyapbd>t+J;F`R>+U*D^!544)phgX8OzBVlT^^FASOYY1Blh%v~ zzdn++rhix^Ti<0^Hf31H@u)A)1q~)`18l#(4C*ueLlIkFG4EW5#J`x8L_XHX-xfuH zgtG?rj(5Hm`Ovftrhmn}^S>kC12&giN9IrNvy#N;p-ZkoBzb4954%d_x|)3yi@{jz zFEhBH!L(~^gMR$(;GMZ|xDW=|z2tkwB+l;**QYS|uF%JGtW4mt*)}|vkY^rc5`j#w zF0#qHm>_l8J17v-Bk3-wj*l6&?JJkAJm1_Cla^NZR9KXW?R_EpLR3f{`zj#kv0q0U z`@<$+U*@wP!EzYbaf-r8!#+Xnd5Ub8`Bo^lDSkqc#|G0sulNLkw!g~Pnc!P8p>z0Y6Ok*dC?Bdngjjr?$`dOhY3xCS%_^yk4dw{p&ipn5%Nj|ZsF zPHp|{i=2mn+%e{PcsW&l3rG$Zi!-3)wzXV@zIA8Yolqd0c55Y`t zb=#G}d&``dnyT!n*sZAFrkvaPoS(=YP&RbJ2lGP2@LoiQ+i)9W6xKH*LKW6;BHbyh zIUBN3Sl@)**i%^Vk20#Tz5^*;DXf!HDZYO76xJu8UZ=1=nbruoOj2R}3TsXj)=P2U zWfj(6XO9zw^|z!iqOj)CMTPY>Ozm7@UCpG!hGWg5icD5M`B1AYn@4ez)_aBZr%-A~ zh4luuV-?o&DVbGRZ^oK9g*Evir?4jL;1t$pu@zO8Pz|OY$;0I3YpJzK2V+UXc${>1>hgnon4XY)HzWE)oCc|nC zqHhgnI~7y1j}ooITC~HDeqCyrJ^-qnAo_Mv%Z%(rY|kpJN$Wd>^-pM7h4l!w^uvdx z1t_eapxLv+x*heuyA;-w(IZh<%U6d^VJ!}VjD|XuJBGt$%uKGCV`0p)oP6QfrNa7Y z4ysdF??=-qti=_AQ&_);sZL?NH^8M5&)^Y-H3hU?D*M4Er*Hlj2 zMfFBuO*Yk2SdV6NPGNm7Q@c@EleqO2)}y(SPGNlmi=Q4%Vl2Gh^Zv?HSYOI$r?CDA z>v9Tf(O)}-^$(fq6xMICs@^KBYuJ#duzsC&c?#=C*-cMj&36plDXjU8(r#5)%Ll_w zVSOdrZ;9&hjwq_QFvxI$CoZ{A3=J}^u;*cSQhV^VuwR8UfCG4}8ijQm2j%_Ip-2ld zx8nb?h>#M4g9Gn|9S)>K7u+Tj_fB!84}4tWha(y>-t{HsK6DIeh@9ApU7 z2knb{o$xx;8!nVRljUYG>Fgn(V&}8|5R=)5Uq-*evn30YdD9V|J*N$z{enXBjmPKONw>IdvZgX;Pal~ z8of!ajeqff5J{~%uqI!#q8Yade8JUgC9OXvyv~EI^Mfaojwi~{TFeapkH|@`Xl`AT z-z9Y9yG8zLSLW}nciqhbS94_7?gZEPcDQ(V=Bw=`W4UYOvhIAghq2s+j3u8}aSg|h zw-5{y@$)YSxG9iO%vs(8j)(8!c#UFt=EH?&2hMU6;{3q848@#fSNW=u*OXy7=DWm; zFpJZQ^6}exUJXq!`Hn+`7acsw^+4yj9vlO+9>tvH%q;3Nl^~y4Jh`a87co#A2i8Y4 z_0?n9l)+-K4i|Bsr(r&ZbMW)~_X_IEBaZcvX)^VZ2l4B>AN7qxoCzkan6oUy_22Z5 z_YtPPV$PC|G4rM}ps8;Imi_(>LVdgkVgEo&+WLw)%M!fXr7FP$*2mu#MHBx3NJoEM zOuKg?1MWUsOuj!L-!NkUO}=8zvK;Shd&5~y*)QnTeDoj0{?b|hyspTi=?{(F!C7MW z^g`m(yT-=&z2P`=PjX>baF!f*h8g&E=EHlWrg2|k`dFX|j5GJBmDUHhf68xbg=Ml7 zIM|+oqSVOF1(KKKI`4}4=nWsq^9p$h9_K{S$O2cWy;1RFiZ?3WuE-}WmU~1|UTnfH z<|BWr>7tQE`hM7CEXQwEiK3AOibfXLqVb}Ug)JIcplD=)qLBsOtN9*N6pbw6JJc49 zENs!p0!1SW6pbwKIGjh>zG!5DqLBrPMiwZKyg<>&0!1SW6pbuUG_pX^$O7dJ67WDi z;sQk@3lxnkaK6SbQIxk;h!>44P&Bf@M>JhDvanxKTQst;MI#HOwutSBMiwXmLyXk>w+kp+rI7AP87plD=)qLBrPMiwXH?t>3=T@W#WqL7g>1daQufFDa#v8k2ib@jVf=rk~NPSjr6iq%=-%E z4e2pjJu}l`FL^_uZ93--w<3$-1~b_fo?b0)C@h^?z2^o-Onb&bhEpNDi zX_hzq2>WnEvc_du;jNBLtnzFf(pzK#zi{%Y-?5*Vu|H`yN-td3WjPQovW7-Tl zYv!1Wd)k?_=pi)w9!5Lf@D8@v18;aRS{B|=M%MC%;t}=qq_e5)m)WT04cV8S;|-T# zo02!ovqP3Q6r!w{H>_gijyHT5-P;M?@XH*>J2|OyR8f|dTZ%4t!y~wxeJNSN!P&v= znB@&W!#>_ka%z5_H z@`mCi+VO^~nYCx$a2(eB-NhT;fUyzY@T*)s#~V^DYMH@Qu9c~VD9q&eRf09ka+IKZ zo|(6EFq~)RPcgIO4L32f;|-A)hnTW^lXF4>Io?pb?mFJ^0%mr+;d!jed1k(q2ae+n zf5*&@H!Np29B=pvGdtdpN2bpk?!y&6KO*@bS3DWzu482$Z}=;wd%R(u={xd!( znfd*q@P={%aJ=CF_Qvssm(%QqH{1)o70=A2?6&jF{705^yx}eER5!fgU%3*VXJ(nT z#_@(fVR6SB{(_Y|&&+();_-&#IlPWHGAo9D~My_d&`VPPM$@JjTtI zEKKGt5qQIAxSEzXoIoqspa1{ac|$xrybb2?;UJtdC+@c_p$*G0mcAyE&ow4+tic)1 z@Hs=q@qLTD-;j(uaE9$olx;RHEhG=|zCVeULaYYc9Ra(X26LxN6J*;`d|lqO5-L5T zfGU_BRmeCuLufJ=?W+1^iDnNid6R&eH$k=swYBHht!funYSaUI(lW=(H7{Ex8bM!Y z=jI_5y2Q9{Re5Kkvw{oMLQSE}yQ_287U;aZd6hu|a#GydWLDF;r%azyJAdZ%Me`TT zI&Eg{Ld1&$RyT93v|vPwSUpVZ+eo=tAEyFObdq+Muy-Xa31w0DhccIxRw z+?GsaY^8F`FoQJatnP#JTsxzzk;{?rZEMsa5azdt9Bgf@g9gHmmfPD^@Fx=^*L$O2 z%X2si4D9B~UenxE+j!y1HMPkE+@0{Nh9+)kZa!}%_S|H4egD@ScgcRMAtHpYf##Glr5CR?kGG|T3a?$(YiIjJ7E((x5H-u zcB=)^ZQ4P4)&6QG!FaewLBGTAX07Ixjg9 zxJi^RXjs#{y51Z!OWT@jSJX8vU4@|(FWlU*lPS;bl*iOd)|wTTGYT?`P<@B_&z0hD z7j<#cvid&`PMkjd==^Bxm}BxH%g>KiuZ?5!qo+47ZCrqF3mO_**XEC(KQBMNy84Ls zPZ)RjnBaH{$V6s?4|K3tg5*gHXUv-wq$9lJt!!JndQDwDa7}B1E6ftAuUyl3;hLaQ zj*-gxwl*w^O0llKzP0iEppsdVn3YY-ntglK%BDu&Ue;Q-x*(#Zy}k)isM1wZ_YTY| z+!?RvUm(n71N2q_pY%BSIK=TfhI;|aC-EMSHg3HnEKxMTG={_Q!>hp_^YJ~ODTBpe z34Xod75U!0H@xCjt_N0<>%n)4TwnI9m{%N%7c7&!j+n(_UhxeK6w9+dqN%SQ%ccw# zgN3*u@cAh-&c(bU@6+yB6a4eI-@SxMw&ef(`v1W33C9FadRrrkS{ihZg@ zvjwjpUvGHD{=MWCC+-*YidS5N{e}C638sI>Ht5Ii4qg$j%)JoGHW_VW{N8XJxhJ`> zD|khoI~jPxN#5`fv66q>KltK^XqhI14SA2zRLedQdjpfTU`+_{s+kFQ2H zgr4WleX|qJu^fJK7gLj%|0HyY^S1|6lX++-L*p1g--xq(|5Ww0p5AdI^p4j;uaoE< zgX)dc?mSSgzHh06q}CC0*1k-9qdT3W&O;kJ$z#f&s&>!(w3s)xGX-Pu{In`)^ya56 z)HhBcA&wmqw`y_~Um?puFbk34{kSEHZbE#jH0I+T$jRFgpDvY4-J2{_65Rm+T{#PR zL01lkmGG3Q5v4MO95bf zRALRuUc_3CPOQAIZ;YpB4`h3mr-Tv2j;H)rR_b`l!RUY715ZiUs8@yYqGV((Pk9h) zIz8z^D!Y^wS)TIeZ1hYHP+YMo!~l{y$y|?rH{d4&cyO>!xOa+Cdr>FDi8M2DqHl~X zPf2}ayj=%#z9TU*S?U{O z%TrF|I2Q1f@m+~-usqw&mH1LpZgBP$T9&7L0PVz2iB*&RFItwTJcRxH;lo%H z&LA6!WL9}f78v*>|4|t>kS4~P`lE=X5!$Ly>N-SZNKM=G1{Am7^KIPwlW3TR9^o5_r*ao^)h8w1y5V*8B;<_^ zF`3G77FcU0`bKM(<@RIQU z>DM@RJmn~EgV~ySM()FG$nlii+hWI4euJ4$(aedyF?KxV2xfLXC6COwJz7Vocs$DS z0V(!)$}_kU9#8oi(>xDmz`pjAO-LyrKmUun z;VDO;wE;1l++NS&ay;cYmUBEM9fihS^OVs`Xw~qP@ro$N&jRAplrs);|H9&qrz~aV zj;FkYnG5xe@tHc>C*`+AZ#er;N@)hTig&F`V3ffCJ}v zO6nWq9(c;(XhP2ILxV)$h+Xvn9;?_)&)~2iImVW+T#GuQ;C=l6 zuHh^DNFS-I*mpuDS!tZcu57Mc!TU|)C>DEwbrg$b5FCkHS&TbrMr1}TZY)s*`^Qs3 zSiQ7*W2R!}3Vf6qV$gY{b;WyfdkX0Z?+kdY#%2+wV$0M0FJJCR1Ya8^NoZ69i<+=U z2}$$FtWyMIbh%jW$b(?q^qeIw3UJ0qtoWNN#1-MUMJ zkaqVYQz}dsbCvXS*&D9%rlOwU z@+F~|tL!RYHFBFWEXaHx@*>RQw4!_m;OfS!o(U!&?-RY~;5DpAIaZSICvlHtxjXVv zrVJK?Gr(1L)^0IZnZlJ77h0*0$nhiALVbBt8PL?X0n2{>Mtc40j;lNd_1$e&QtM-# zTfDll82qM}TxA8`-40^`4W@r6p4>fY{PD>5fXM_j`H0C4P?ET+mt5st*kAbC%>+}Q zu?_n1yMwFbKITFgV0R6c+6ccl93SpUF6;`flG~nP1%Bir4R^^n?!W%NNxOmx8feR{ zDXu;VO_|)z7+0*di3HNW9TS~4dC0R+bO^RF*R?M^ehEf&50)HHcm#4OPxv|(!4uvG08cm%FLa7|!igxaJRw=1 zu6V+vg@~^oJ)V%_6~`0Kr!`_BJ4BxFHngTZ;pwc|@PrZWzmzBZ7_*d$AJl)xB6&i2 zY2P_d_yDpPE-;goSGmd>mSvCh(t6JmE<`Evge%ZL#}i(J(Q-T?FENfMJcI36p73c} zmM1)hmgNb*#9Ay*_y*G~Pxu?A74n4NMKhTYJmG&Z&GLj)yLmjJa2Sp!+`w9U;0Z|q z3Qs5_Yk5M>jOTd5+u5k)3E7vO;|b5iMki1BXO^@);f-vgm?xw&NRKDnj66HR6Mmdk zTAq-q-L7~-GLeoa`~!1Zp73&7mM6S{IW14vpDkFPkazKY;R*Tn+VO<+huIZR$WK%q zPbloBv*8`%oCPk4c=Wm;RRS@;R!#%)pI=I5{`u9370U{n3>#Y zZV6+S<%Tn};|VDs^LWDdGqd9fmou~D32|ZedBV>!v*QW*9PaUi=P|S63CW@QJR#e4 zJmDcU9Z&c&SHbaw&oR~UghI}HJmFhR_ISd}*|NtIKFr!Yo{%48cfu2%%xXNI@HS)< zo={Ezjwd{k`5aHUfo3;6;j0)W;R#>na58#xGg!E+M@q~-GX&g@&v#jF@`I^Y%3F*DW;|ULAS;rF!?dyJHr!Z*a62AQmoo1o{-0? z;R$E5lkXHycnx|8o{+OWD^JLK=x`OIkGDMGRE(qYgzuH~`AQ827b0U6oX!7x$iU9# zu#{IDM)xRkwYNhDJ`{xW?mK)=BW~tGjCprlAh#JbkV~k6%wrjQGQucqUBd;s&%T+0 zS9#`km!^cNiiE;>+&qUI`4#}v4Gn9hm{E*d>ECV=#`i*|y@E+mU5%$hqhDEOpck8aUP z5Z~3hMgRPdI|K>ET;LRNBIFvS-f)2#aDnCMP!4_A8$WiHDUz%`NxGs5BXO`~Ds`uzZ+|kVuI+afD*E;xUTT6lW<4e~0`l)ZVE0 zF~u7dZ&$oe@e#!zC_bmyq4--xKK8Lc`zel86yK$Y7vH5o@m&fO-=#qDT?!Q6r9i$9 zWIgvPKBg$XOA*h9ey0CU@lC}n#)k3YXbdR6OMxe9y!bAKE&Lr&_&ea|H2sT;;=2^_ z;=2?mzDt4PyA*gJ4gj{p*S7V7qcPUVOmjeH+ z@uDMyy*~~*<`bu5z^By~-=(l$Qu~jJ;=2^_;=2?mzDt4PyA&wCOM&9M6ezw+fx_Pb z#dj%Ce3t^ncPUVOmjcCiDNuZu0>yVJP<)pH#dj%Ce3t^ncPUVOmjcCiDNuZu0>yVJ zP<)pH#dj%Ce3t@6M+g+(r9kmr3KZX^K=EA)6yK#l@m&fO-=#qDT?!Q6r9kmr3KZX^ zK=EA)6yK#l;qQRryA=329D5vp@m&ghRBiEH3VXZS;=2^K_$~!b#|4h%#CIuBe3t^n zcPUW#JD~7)z+(RHT<*`<7kQ0~aKe@29dr) zNaj3z^^|eHhNtE2&;kyWxeYgOej&aJ+P`(s3EsW=1)P6yf<1NnQN)(3aGE%MJI;sw zgZ$MUG4zC3pBrD(IS*M6R;~Z!YUW$89qsgwrfz@CHdv$c^sZWe(QEz3tuipxpn26w^MT{{cYL& z!XMvytN*(EX7v4|9YKCm$Meu4hSjteZ2t|?g6bPOHY`E=D4Q-%+GW|2>g8Jll;4It z>8TA{|id%v_H_{)Ue4bg6yxRgP3Y zDuJ1DLdZSZ$8NNbR5z;FacD^pXPn8o9X<2lLSGb9JM<=i6qrEtP4y@AqQ7#_Ngdp>2{M(FQKQ6?kv6vSlkfKi_l1^Ulh8YiK-H^Ox zspE`CoGVr;h-zq7+(z>%Tr=WPq^ZhM(NGxaf&T*|{=TfjUVMdoDSm^~W3q3eRQgcH zh>GsKAbnU;e=2(;lhn#&`4-ezWm(cr#u8FAJyz5bBA$F5ZDr0wju){YYXs#U`Qk6& zH!^*A_Foa79&gs;w`^yEY4zt!o0wRcEU$*?BND4D`#c6OePm*lXFpAAQeq9tew(#S zPOQP%$+V72tRdOkSj*9gl@H3Py5wz5CQK*d$@C0HaZC{#q-TmOlTKduX2BD=8ZtQ~ zIhge9#N_0Vr=Zj}B+5Esa!AtLoGb3CFuf=l+0z)sia)>(ot|_dm2G21XWS%dnJoK~ zK9d8a;z`zQJ57~jLbg7w69T! z6;+JRev7N{fOxCb$syAZCM{IUUsQM{MtrhozRO*-r!0kN!}s zWOB܌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 z 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

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�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

81Wqb<71CW_;{Ky2MBiI80naO763&>1l zs>^7b{3TtHnK+!~feAwI;pSl5VOnv27_6{5Sht0m_9^i@(_y)k(s1ZJ^-1C^Z(g< z7cjeuD(!#ob54>brwI*7BSHeFlORDzx^p1}xfnvgkOT-JTtuML-RUGPNji2XTpUJ` z1Vumw0WY7U62uEC@;S)KNSl`gUe=fLsHndJ(1*X={>b`pmCn(4ePAx@4Rdc}N@9D{^LXzz0=B zx_)V6s?sy4=W9&18fPAO0c9>B?}Pjf!V${3y4lht2ff8_o}N^`Oy2X#??_U6U#DkP z?k4^OzlQfp{?Qtsq^CD5CdEZG$~oYNYQb5`dwZ(AYhdPE@HT6~+1lylw$rl?T9c&j zP)DRolJQa)D0ts16SPiGy1(TOV~2z;1(Obz>RbGVF z%7k>`yeUmnzr47yy>Uiy{PM3YY?|Eo+TtGbJBoWQFP=Dm+Vpvijm3*Us@TEHH?ELE z3;Ha@{jZqD%Ca+x>*p8CQyMq4x4(I1J0tBh74H!1km#A=gD$aneFmSW^ye(E?z;4y zg)562OZi-}E{ctoH|DzZU4?fRuPkjGcHXG{P9=>Ck)YKk<&Vb^wHb%0dx6>CwpA@1 zt3lCdy4aztTh^{$>r%Fm;@s>aXDq)=A~&oH{yRa@<{mt2#9ps%jhJN9YY5C~#-Hap z7se__Z0IHHaMZ&bvm=i)_BtZAgKC3UX^46IoR)1iKG@Uoe`XId-fzAUyW)|Jtq;l) zf$TTUiymjh*kB{hgZhf;UpU2@*ou0`UCCtUI%v?mM9V|;xDZ9A8?~s_Hkn>drl*o=Cse7r z#7YA?7}GMy^j}Q-M#p8^AI*;a&ov{~p}r8<{T`|}RkvoYb&+c=cnzOk6qs_2jCCtJ zy6yHs%||Lv=ppp+XdOUn#-0!O)X_{JC&=skytp+ea`lW|e*K8QLT!tePQN04WBB(- zwSL7|66Wzn8N-j)TcUEjz_!z`cv_iJ@X^CaQ~7M1gSw*6aAke8iMZcZmdbrSO7NR= zs>;d7o!+WG_;S_y70Wmd%^~SH7*XcOVS_wh8T=?;i_#_c!Efpv9_=Q1g2`tcm*fe3 zzqGRL>#&)O*(>8SwXNX$xmv&CY2>L7ByXfEC%O6_jpbd2Jhj;epVvu3+`h4u&_0cG zeEZ(Twy(ox@;&5LwBdr!+dw>)H?flO)@mOVM=npCtm>%k3cp33d_ef1JpFEQ=+plh zL9=`$%KHr4oYJ*E>jlaUMsH#qgu+*RrAVIkosE>T%y~FSxtv7N9sBT8Q|tKYSp7B1 zX7a%P%(iR81F9XTEVjo1X`5E*>=B zIAEJAC+0%sZ*}-thvplHbn}e^nr|GimK)pTJ2c-oq?^eeXufg4r7qum;}AFBIN+Tw z{a%OW8;5kM-b=3e#sO=%NENp!GthkFfaV(qG~YPjldjw!9GY(&(#7<{Jkz-#DQ8#sSSY z4rsn{K=X|Qnr|Gin^{G*cY{NmxLEvFhhKE~Er&mJi1QFDXTEW$Z@zKB6L{fDp83WB z%{LDCluQ2)A=jGu$N~4@VXX3F9ZnFEeu%@@J3PT*v%@xr-3~8yc$pA+?{@f6ho5ly zpLX~om;SiJ-wDyn((_6C=ardYC1d(uire#rxSnr{FLe2fT-=^p{CU)KSmnCf`RZI1 z6b=UR*)yxDscEqK1{3uH=CQ6eamLYO9Gz(0jRda#?xsh#?16ukJ>a`d=K#MuG-qVI z=7u=;|8;r>*S1K{KwSCat%O&>{cb5G8}FAa412ycIY}P#dJ0X*FujUJ8;3oCUcv09jo3Dy zPPzBjtBoAv0mr!g%g@WKrkm=UXeasydnZjB>+uP*zi;19*p^O)`bGO!JO4jqu0sc7 zT^;?C+-N!!kI-iYSMJMf`#RED;L_V)b!+e!r1oliKlWF{?<m&5kS_tkiyJERo!6zjpS1Yik z0CKg0ngDYUXrjz3XvpQBgK|l32FuMrZLbODG$*qTO-AKHdiYU@mkE) z3O+4CxmtnRvSVwcNMS{5q>NI{T&>_%#j4c`KCc}IwE}6l_P$2SBn}_N ziOkgsM9KHHM#@*zbGcfxTm%QRYseVZut*O_iTAo{5f0pV7wF2?L(HbdIU1E)t>})jEFIC~7R`5f0 zWKb(utK6Vgutd2*t>DetZ%`}vmUa}>3VyD_L9IaFMXZsM?RQcAIz@w8L8}S}wF2Fl z@fs;_mFP&Vpj!tKsTCY0X^~pNfr@XhRv=xec#RY(5ZM|j_5cWK1r6GLP%Du4o&MHH z8BgnMjg-wQ7}N?@OH5EJD5%l>s1;12xK}IC8Yyy%!x||KNSA7fKFp#uQuG}et&w7j zE(Ns$eWkKSO4i%+>c1gTL9IZ#`q3IGCrffrD_~Ha6oXoU+(txeq;yDBq*idWWJPKP zdnrFsD|nw;5~&qDAaRje!A9jrY6TmVAE_1Gpm=|31-DXQuxbTbBju}Va8N7wq4vL{ zY6V=XuhzZl*GRcm2mKPM6%?N50Mp^6ZrWX#riFP97{{>Z4tALykm?L8=#vB9emPwI ztCgLej;_M5YnYc=pxBRTY6(!8V7#tbI8} z=xUifu|+E5P^!@R`=<-qryf3aS+REh)V8Tty>?pT$Zu!GjZC!qrDh1=KeK~3Nxd=oerT=&{kU=P zJhN<(dwOmd8ntNG7}TPD^usM?R?w z7YaM>TPoff|Mh%jReGPVmDLZ7c9nv{236U(YPSJaY8j zQhk+Un6n*sUz@$4$jO4rDpYg#eU;Pvvah3hDtBd+;5X+~m6Num-iki>a@E}ZPdScB zG+xJ{(Um`r#ccaJlJO^o%T zJ}J-5t{>g!KB&C-Y9AEW{q2L|^L+$V{H&_DCz$ZkmAyA6nkR(i{w3na$-(bI_v)4l zaXN!LuH_fEtdggu?v6F`0TsUjw|^_L-^2N79h*9iZ5-VFTu{CamI}F`Rtfh-eOySn zhe2^_j|jQ$ek9~#G>(i}d)=1>ctjnS(sk~H`WBE4uyOq0co%PSXdFN3b6osnhsN=f zzS6}vIy8=-^bfiCZ4U2pc(23nJABNcCZp93WT--Q*;+o6SevIP>jpGN6;|Go72aV$gjpGN6;|Go7 z2aV$gjpGN6;|Go72aV$g_vf9W{Tas(8pjVB#}6)b`Nr`RH;x}Pjvq9RA2g01G>#uM zjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9R zA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01 zG>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uM zjvq9RA2g01G>#uMjvqXM7rw3!~Ip(wIusxC7QSz-69hIPn*L^UjQI;FW_+qnQK~hGG8o> z$W6r?sb8!d&;N*zkkf~$cqyw@xz(A%ZI29GqYG-d7<)-%wl88_6n8XI{Ie6 zvWV=kmc)Oi#KKwpP7o^qrBWy{B+d|rs5*`3W3wTNqZc?RD5qFn5nq+Y|iQ0 z8}00@d8M-0*I+8Hw(OXx_?aB0OvTL_HRp6cr^ZKXNnA~-;zSAWltQSPihq$Zy_<@= zwq>T`S)``CNg{JjcQ486!&Kaq9X^-U+plbf#yO{Zz3Szh?oV}&a#QhFYY#c6Yc2?S zaJv1Qif8*fru->M$xX#yn-c58RJ=(gAI$14EdQh0lCLFEuXg69;$~!@bGoJTRMeS@ zt9oQAt|)AWjGjw!^VO5n4Ta5A+!Y>-skkc|OvQ6iJ7Ow+HO(?p@%QWS!depIFmq$@ zQoShbJX%wKl&*Ep_}11RrNUt?iC3jWgQ>Wvs6tpv;;kwiI9>G)rs8URVZyBXJ5)Gu zx(BIn;B=R(R9H*meB}mC_y1_WfzutYXjn_)k2-|F=}Pq`G8NaI8Jmg^SLuk;eNcOj zI9*$5CE|1|%HN*TJz0B+OvQgiF*6mn2SDI-=crs*OTs<{`Y{!MfnG9Gap{#rrs7Xa zOyG2Xs7Ce8>6XlUS!61HxTKv`(7QvN?np@srsDQJ7EHx8WfYl;k5k>Smc&Z+XW(@I zq{6{eoUg$oG8KPJbpxk+g6alS@q<-3;&i{Jc*N;8tC`VS66+)_;&c}&KjL)l`z+#g z%_~C0>B><`f2QIkdG8o3rz=zOq8bo5-HnP4!0GA~Y-cL2d)1qYOO0ZPrs56A?$=bj z^d4H64nLrQu}&&NI-QqVCF)i9c?;|lMZBhq#;sEE6ZQOp*F9jqYDq&RU1GIVL!%^? zhOmN*t>B{Mzz3+7TWBZd6DAp>v|Wb3-T4fxePO%^gY17!(gh2quDJqjjg+qHmoHVv z!Fb*BRfH5v9x5th;nAKB=3XpYtIwnz;{gxbi_+N^>E%4AMwCT=TDNbLs6FNVft) zfA-Pt7S}2W&OCZ&nKpY9|J|m|c;|0vO{R3O6wR!yuJ%<^)@gN!DRzTQY3BShKt{j<4##8$^`9yi1RQC}HjTRy`dMsr_U^T3!m*@xAh+@k_zKDysR> z&ojTT_mAf8_5M;hUOwCLqpL`ZgUTu##!uxLURda>oaXv`9Til$zl{?7=A5c>S|m?z zL?3)P4R)jCA;|EXr&h^(hK|y4sb7S?Us~Drb=XWU;YHqCKVLvz z+`i|KS0PRE#DRHv-E7D5K94;0sSiG{lZ05_evATq`>2x3tL8`lmCTA426}lL*pB5@ zkf(W9wU4RYTwb+p^B<8Xk7qteo_@DD^yy!MKZ^Us@Aoql;9AkdoQ2;~?m!;_eL0fz zYf9O*%-0TvADsn%H5(z&S#~^Xs*X#?>aS5YlS{Z>j?{(=e!o8MF;0Iu`O$3uX|g99 zGkjz6-pLYP3O-bQrBLA~{u3HG56YX!ufk8y4CTkys?~Lq_TD%-ywUMD!t?S%#6i8rKLK*9aQd2pZQ2+5`i5t*d8T zBXOH31&wP2jcWvrYXpsJ1dVG1jcWvrYXpsJ1dVG1jcWvrYXpsJ1dVG1jcWvrYXpsJ z1dVG1jcWvrYXpsJ1dVG1jcWvrYXp~a)2SbfYXpsJ1dVG1jcWvrYXpsJ1dVG1jcWvr zYXog(7BsFAG_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@ z5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5 zG_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Dae zt`Rh@5j3t5G_Daet`Rh@5j3t5G<#z3d0u9P0yhs2YT+0m>1Ia^PIKuq9UklQrLL}W z4Z3esPxl`$SUsN@$2XCFY)}WoQ*K90cIwJhw>x5Tooy${ ziD3GF$??SGvurzJ@;I(yh{<151Y+`iJUcxxspU{3V)9(-IAT&$%zY7)S*?;D))6r& zVlfbtA6IPr5_XA=U{ai;sQIuiu43?%6O(%o$$VIsbiX)a@+<6I#H3Bp#>S?ed@PmU zi9jE#Dc?>7AFD0fThNh~gC`~*LxzaS$JOR(4c79c{4d&1PE49h*cn+~P5C2gd`?Wt z+&2)DMq%c}bt(vz61(3EJAdvTQ_dudWkO#V?~0x@Z0fB}ff z8<1qgr1fl0OshM@IXwiQsF>Men=e|h)Eks24d0{4hqEN3hg%# zllSO?3dH0mBq|V-|Et1*n0$+(ftXyX!ok>7cVr3hY3|#HW^CF(^ZO?z|Azt)lT#S$r25oOPfe{7n+p{^f2>wAP3Idi zdDd7~F(qPrmtJQ%=3Q@#F;yCilf%>Kn=joeq3KJG0J>#W5~J>3YV% zYhFk1M?9uHJ2@UxEoRFPG2g%jtZ*Wnkd0<&Z#OV-zj<%`ryk|^OwhP9Q)CD9f$hBA4j!) z=ewC(IK~U1oZqbGFPB98ppMVWYmMr#nLJR-yIUJBsGLGIe|aAADx|A@{qdKz%*Cot zeemt8<}WXc+D8-e_EqzjMX#;UImBHOZ%cE+w|>Leyi)m1vz_F=uJ(`I&P%O7KC?;B10I_*-1EWnm>%`a(sQG$opoK7?FgSat*K&R z;B}H`^}=VuU2d&|(-ddv`AqrFUF!Vk)Tt^%ky*%s?!~Fxzx5ZU6XVjIi9)vwTg^r(7*IkU@k8NU@oQo;h9Tm zkVMR7Bc&a4c@{Z+F_&4b($N%&m`iEp1m^M-#l|n;-XCY28=D>tdUfKj85)s}6#eutUMHzHBYrTK2mnM-52a^`X>Z4V}uGENQ5 zr4b~#N#$(i<;-OZT_2dsKPfL~ExN{+o6cm`klg zxdY7Qhb1y+E+3GrL77zkK@H29%aaw$nadk>#pcXqo%WD3ms$*QD9q)pl9Dr*+q8#1 zOe)PRH)k$w9GWwi-_!Bs%;n9B1?KWR2_2ZZ%#;n{)pr}HnAu-y4u7{Oq5Zn9KgQLc~H)KAu#@X^|OW4r=$`2$2oCY2Yda9}PklH|Z#>VPBW z@?$C-m`kk*-M2~Q87vzlzoG1ix!fpW z5p(%%NsE}vit@K-E=v*bvvR$d`(_Jc$;j!;ACinBke5T&WtS(j!Sv zATOH|@C4@U%(BuP=FYZ)OL&Bd-a6oPRXaq|PDwS3u#_Lw)vX<#Rr|mc{0-62$}2>m8SVXc+PW_73?*aa{hnMg#hQMPcC@Q$)scvtIW)#A5L3b ze@pYNm)~CV6>4hfdUUs;WyVMLvLr26;9w0chjxE@@5nZ_(Dn*n(sq0Pa1b_{;U?ep z%rrBf#ml-on^)oudleJWYmdm8`QU?AB)#14`s24Xt|@G8$G3BI?VLnso~JXli{@_I z)9z(x7oRevW!<{2j@I?-F!3CG=#A@)UkD6IEL3xy703Yb0eUBCgrai1c(&s@Zz3%Y zDyvYZ#nSD8X;esVb+poZf~$_;MOWN6E=74x&QibsYV1olD_5ukb=B z>o=>p&T}FzQT^fjr8TO$-**B{r}iM+e~K~h%o^t;6&VBt0JQX1R%;P+e2 zbzVTZ1AQUTm#enbyo_>R>7|^?bBW~Pwn5rzJ_*-3Gp`l)8ztGMZ;v8>F0S*uyisv$ z7=^xsT&KpSy>XorNLQZDt@2+B`k?D446`Si)7j|dwp^|P7|QxVS8S@8bD9egA#PL0 zyxUfDx2>klFY(OTZL7)sKtKGY*=m|!SdPnVHNhe;C2@@-98MRK{yG;w&f#L0{$?JD zxo!h5R*hv8KU_#$;||3=m+4W@Dy4Vfc_%QMmC8VCu^zbtYq4I=JB!f6MpJxdGTYf^ zvNmX`>84?6(+P#7a?>!@BE?wf2fST3k4`EZhk1UpR~yUS>wFHp&gUR(EX@%X*MZKH zysey3b@y7fDsSEXTvPXq9ie^WGmvVmCU>y`C8Po=P1zjL#s zG}w^7MuV@b`I+;#Vp7QVQ20;n()oI2{mv^87}oE+TCwpvz^EmapIW7D?&3e&^5G z&A{t-{=38kVzO38JpeKJHCkoscUsTp#H36a!}^_nFF`pmskZFc`kk`J()yhbs%B11 z+NyTd>vxI@8szdt>1Z=`mqNwS-kzD9B*MlU3tBllCR(Sv|>3i`Lxbd zPE5X8v7DH^LG8&-FWNRX42PDaDLv{m%Qv!_4mK6ITCdackTfg%G6%52=v%~~q@;{Z^H!)c-&n%9ZWMRm9>2^fx zcm7P(12HL6#%TS{%Tzd+R7(3MTE9~lV6=W`jp_zsQmQL`uitrt`Z5rcCrMO9Oiq@p zh?tb)+hVkS=UlZUA|^LTTtrMxSAJwtd7SbiV)7Kl`ynO^7f@iZ#H7~m{G1vbh{^l4 z{{e`}26kbu`*F#BH`OG`Gg`k>_bR99hupTWwna^cMohW~$M+DsSLts4Yo6dw%XHcv z3n+%Lch>K0r`>M-&I1`gr{81$=|p1~DPa14j%6Hg`JRtVOrIxt6UVvh_-)R0yPkHhf!VpftF683(#r9RPpM36YC7UA z(~tS7-iP z|Em1B#!r5RV-~jK2H{8;NEj!?t3T(Mq*$gnqX!Es9ePlnLNzzoS2@km`8ukna_2`0 zesfM$IgPvYrt`s<)A%n+P9||2YWx^L^M5*C9oJ%hz7CtoQ_L6k=0CQY8=OQ(slMb1 zy}VYoV|goiaZe)62j9;cZpQ6vLZ0R$C66ZL@-$f#%ex+V2a@K4&#ShBoE^1KV+Y^9 zYHsijGAmvf=;djA9k*{@)V}_>!S5k&lRro&Pcm)0~+~9pA z42Bzg1_Q`C8U!SJ@l#XP?>bh0Ewc&PklDuBHh;cfeB5K4{&H}Gy4FI7_pbKq$_Zg- z*gZm(ThOyYT%VqvKB(Wa$#b(})WXXeXodxt$yR&EQc(9?VOXXg*QY%q*N)jEg5y;l zY;t(CL*4&MKiT2=4p%zd=#~f~R_#2139V~xehZ7x| zuQlqKr#aC4t%9b530~{UnW{AL+gw~<)Y{*D4u9Zqn?ucSDBtW6LG!l?+E+bj{#HTr zw+foSRnYvcg66#pG=Hn0`CA2L_aeDw5&@dORZv>>N;iK`;7k`cf2+hbv7`K@4%av| zFIl9Uzg6&Fmu~)6i9hY)=5Lj_ad@C{c;Io|c#`{wL-V&v{7Dx#f2+jJ9uYKutDyN? z1%pQ59bm~ zYl1)$B0t(uHdj5#+Bt2xg zHW!UxI^&QAZJy3G1r{=`jGeV|Pglp96}&^TlWkKcG}L;ecxbwyu`pap)m%<}&V8uG ze(jrATl@G+?%=0R$^ zImdX*T4tK)9)6CK7A_Vu30`3<>`Eqwbo~K5E;yO)w?J+nD44PM-1+}>OM}YW}$uX z{o>;u~N@K8q8R)yS3}Wt}xUa zSpRD3%o!C61N)TNybq1wHl1NGDcP4V*e3L^X3fU0ebc6`<|}`Z{Nr!hk~D4H=6&LG zI3qOp5?9tgx7+VMnr94Shw(l4Qu)2#etqs};CxWe;q@_GycOqe zf}!>6Yn70k&7+mmNXQ1)^K6?>ND|X!MEu}d7Q@cn#D8mZn0cM(-`#}n9p`T1?~ylv zyNS1Imth6(U#jxB1~mbv?q@1Ceu>3!lrMD>=WfCrS$KC7d{uZy`H{N``A;9f-Guqo zeo5R-*kZxc{Bd;CcfnEqS`N7{cN4GXtr;BU&*l(}~FD;wk0z;cmhv zRC0F{<}Ez9n~)|(z|z+a;bca8kB2w?YO%M&0Y=C-NaZO z!cKBGF+#F>Xmu5Gt*&ErndOf1d%ByrO7(KBu3xBRuGLl6InA}Yc8YLyw|03NOeY%@q=$1t8CO)OYL95H0pa-q4FQ{;Elwaj;;se@m(CRu=(LUWx z{EKpfR+sL~*irtUlpSexJ)%8FT3yxdCMGF=d#x^cM~vM~{FY+9xtp++zJt4o%T%o& zt*-x|m(1P7uhm^atLq8X3tC+^|J=7$S7`uu6MIWq(CT_v(cmcmBpq?k>e^c^>C@fB zFx3rOUHWj1+)X^Jy20IqdEW_IU6WNf((1ZT@kpy{xn%d_ZsKg^M_OIas(z%^Rqbx# zF}0>2t*+9?xEThk)uk1@N2&oqt4mgbyR^HB;%VFr!#>bJKb5YbvG~kCg<%g{{2iG- zEF@^?<6Tb7Fqac&j+2^jIzw7w=@|!h!P}&>jzn0x=%DoTl5~{SPDiT*!>jA)$#j41 zC`}W4Fejrawv%+hG9%%k&1O084m;V?M3Q}#*qo%-;i3zgZJlF0L9mU5^M%?$Yoh<9hM&Gw#(iI!Q?)9tIHD8##LfuHbDE|?5zOp&ye4-iu z_bpv3Ftl&!Zd=#ELIRqvFJuU<~*EF|Z(y^}D`lFZ0@#eOR)>-egb#`9ZL50?qZvNX= zbpqKE=_^`RuWmt;Zp9k55`62f?{3GZ3JwjDe06~}TCbUv)9G`0=D)PPrOTI0a80qU zIqU8$clENCOF0LvT`bg&1au&S>w`98D>}Q#@6+`I4=se##P*go>(@%qF1$E5YppEn zSiW4xzuWagSaEw7TBf^K9wL4t@MgDA%|m?rRbhTyqXw1J3%{C&xXfyYKb2Le<{|nj zr#VMoNA*-regJ*&n{!C;+sZqDm!RH&KKQ(99^y`pV=#X1FG8W^2rmSoezTf~c$kh- zKWGk9==-HLs>5b-J2Op(Yr_TK&(%D{kC0bK8OiI9hxjS-dYhN8<{_Sl+DDbVebqd~ z@5!u??}IfFxF8y2@l z$J3Lw*ctj>(uq^hvqQWD-INFQW6Y5$o)43qs|P>u6eigl-$twQ=b+;#a2~}I4yX&p zb*6pE?)y-OGaMf6P|phG>)sV=HchzF;YNq13r6~fT>LhNcR9S*q3MEA&c4M#Cj3p_ zZycH~80n@922SL3Xn&>)2AVDyxYnhcE*Nps1p`eN3^ZLZ&~(8-o4N!|7YsCAFwk_t zK+^>SO&1I_T`+L>+S^(G?_PU5>xW%s?d@lHVd}o{{v4`x!4Bdfr+oA00G{gN72P+y z@AZz1*V?w{1%Dp(+)=siww5)^cZ4^bG5EE$FBjdy>qE~RX&Q((+$&*iZS=$@%Fldq zqv?y4>aO2bQ~AiY+NSqB9{ZQCgF8I%g~lzaJ<8Q9QO?TyvaF$9>&o@3XSQ!r9F=K} z(W0PqHq50DGmW!+&1ptBBle7H)TC)TT1EC~KsT+C?2$QXN2DCe&f2>3ByJ-s9=f9 z7w+&M*oHf7r_k`$^c3ZMMmZyLj}Qk_r&KwfoDp{^r+1GKu9nUGdhQU95c1Jb%DF>X zj|Ptr#eEdb7TLa>kx$8UhvSH39wAEKwYHc?h%4EYM~LUCRpk+)fyUuuqI{OJa_;bA zD)?A!c~TS`yhn&nP)i;m^b#)S+~IGupXq+DKT+O{ELKzg2Q@xlWLsAK#hF=NT^Xk- z7`SJ#G3G6!I4g_oSN>n+<=o*1B&8>J_)B&(ut$i!R55sj&_ikS80Id^!BVmWsxW%A$= z;_Etu9qt9mW>U>+fUszVRlVY_SYL(=hoIw zQ{CXI{(clr{Fcl8mq3+DsBgC;19dUV8A0p15$Xp)<31z^uryFr<{3&_`T!@?r^5+1@2Ju9{qTPDE$X}^{(pW z5kmU!c!bca3H_7$f}(*ttdsD-9ezx?fjg8Vdpts9M=-Bm-bf;k5Wi4v;0_N~vwJSG zT@2izKF1=D5UV6A;toq{N5mZ-to(>OltyXn5#kz&i@3u#C_my3Pf~v55yI4N`r{6l zQ(>^&p*%u}A&Wdhd|3M*z$1iC!FC=Ybgz1k5PzqG9-2pp24wRB=-VR%*D)8sVZURv zURx@CgVOk|KLRLpi1Rz(M8-hrDvlz3ja{*lrzst|hgc0=Sg~~q-di&+V-F9}4q*>l za8N(#IRqpJ+$0I_V=w7!lc3rfNqH*rD5D}5W#W#?#Fd=5<0El%Ok8D8+EUCHJP;x$!ZO2ElmGhDGea=T7oEAsUFJ3gi zIC9FB#XUbtMMzW_??8enE*N!r@HQVOfAx+l4&oKS zE8gb*^g7UdT>N@qd}IOost2ES$$rdjkfymr)mJ%q(1)$|@YjPvv=;TIj1> z6S=;Q3aZ@SMG1a$PF1;scsc6L=z}j;&1+uIK}@3YI*#G|{BbO1+t-nddzpQcW}**% zvzpiZ^4RRSBu~fZ<+ZZy>qv&y+g_p#7gSE6n%BGsc@@$nj~TsOUN_sZyrsxHlr$fF zUNx_|Ici^jyym-+SMkC?FK+|eQMV>fBCn1#wXZ*3^V7)twm(QGPcmn=i)VI_{MPdbR3~yO7f$ z4AA^zfaV_qH2)Z&`NshN)0O`phkNkAQ~fbQuEG5rE^z5*x%jyb-{;Z~*L^^Fz0V4~ z^LgA`T+b%Ok8yE(p77^U&#;U$ZCe#lrUTkqPMbBO$xJOP1KV2u2o8^sbEfq?Ddp=v zy$Acs%Fmv-AX&T>ETx+_6_d@IMkjTfwx*l!`Bhr?j>lg}PT%^+7hd2ovv@0h97d*_ z*F0WO-t3<}F*#YX6}^=Gle(X6Dw1GS6jIF#O_D}D*_VKiG z#p7wRcuTVR-d`c_E582kUU*?7eeoSeHCySzk)_I}Ex*K$Gffsg?tSc^Pad|N`)j@M z|54uFY2EOB_&<2m=yH*hRe*c#&-X{n)8YXng-y(eTXCU&nd}igeSp*iB2t|99wQxd zD~pcg*Zery1d)qLa<~2nHd*&7HardaH*Dh{qy@k~h#vCz2XXKp#PARD-YoG2AomZl z2U5wpRcRyWM{18KYDkt2A2y1-!g+Iv41ZE7=j!j^RND$_3a&!rHZL77{~+Wi6M4g(c zyvb9iOr5c|tKCI4yVvHY@yx7%w>`Iw&BI0d|M|??#B|EE#;KF1H8oCNSuD?=+SFJa zJ-_i^r?M<$v2{vg@raKWPn%zyIi+~g{Nl9v%(thh#)KW(J3d>J zi?(TnU|nF*_00w^zq6Bd(kq{`tYuwGGNrYp~J@ zc?n|#-##{Tc^laFbtL1x$g60>1)ukJ;<3CMg!uOH37yNkfo;`k;-?_1_IfXbxHk3s zh(o}FzQHw?@xfQoO92sUefpm$H<<71v7A)BXngSUgxQ-l%NeVKB2nY2z4)o0bzDkj zzj+wT`QY;{4_;oL;5Op)fxoZgmS}(R9u-$>LMYmm8plW2ISncC-c>(7V~Xp{hfqv( z=0Yf155#fxLm6R^@LGo-5Ypv;@9^JT`c@bJqC*>F({+A~ZO6l(NA-%0so`tZUMi+18(9zJt8RMJ z%t4Q-&(Y4f95r8{Cx*t&0!OAx z_)q6sDYop+NY5`{lB6f9;_yW`mQKI1RKC>57vESq~e059P%8oVde9VU%(8lOFDb7v4d>Nzp)$ryVe?DM;FiD#h{?rEIB57hm-Ce}1$Mt=c zJDjqM?k_q5@H&#NBSPa{KjpAUYsdR%`u?+ndNYuwCRDj%1g%C@f~8K2~a{#R|d z;Pd7ak4H`SG2rpz=KjX@8|ffDMt$)8S3RzO7i0ZJJJcPW8HeB%X-cCH0_e9jb{>Jt4YSQe5AVZt_eZ(PPVLm5eFys0wC^wjK z{U*u{W?a8X2StN4mg{d^zlrN*|E#I@A01MZ8v0*}FUPokRG)j-y%?V{#dYRG7|OU_ z+@gY>0ok}dez!8Z&)+!Nh^xSP(ED>RKHU#hISNbmj*Rbh@2Gr>!<7#84XpHw9R97t zjSjDI_|Fb)U`P4QE^Zta@#hrhIq9PAwYNZJ$5-ku9HZ`rei=JoLzx zFWmFJEq4;XllbTFd34L?AG(LMN6EWq%kpD4Z@shb(JlKlo$z?W;hVR9>F9g5jIPXi zyo|rZVfSp>dglT6Z29tG_iVYF^si8U6!YaJY?-{hQ?2Zy?zwTBzDYEfj$PDKYEHl3 zA@Rq@ZX2ilGYcwoX#1iOnAnKznZ^-~K9^+e6h#km(O}?o36BmgZ9?`Hb(L+ybCa0< zL>IP3e0)CrK}9kHr+*?D11G(HF>pGcC;u>K;G~5Xy@6A4{D`Be zzHTPIj9L>5Q_IFQ=LSy4k`tUGzLmBI=ZNyfTgVNZ4p$pyHXv1ttS(UAky&g^`OT7& z8#rkV&O&bBbcgbK8aVxc-PELo6UtX8FL#dEs(QgWqKq#h=ZH6Ij{`VI+!qPv9MO6< zH*k_Os>0b>V@qWX3xab*wWV;bI;c3|s+11Y%9Azk=kJ63tOE~9_D=UIDQVAt=C4IP zTxc&B)u7zK>Fe4@wSm)XC>}XSypJ;H6+Sttd2Leoltl3*J_>!~!fmc?GH@zifmvhA9byji%r$0(^Zs2r-V!46S%_^B2I31`)=gtxL&;j)@a4LLFW^IKD zO{1D|22=QY*5+BnX!JL-b{ewNOJkJi@D zSK(mb^mFw}FmQT@3I_wHYg9NGI57_37ITI%v+6&m!ok34q6!BCr!Ex^22N{~8w{NE zREeA;{<{hX1E>GeAp`@b52$c3aMGO_J4d`%*^z@tg(Cx}jfzJGPMTtjog;QjTD0WttCSxZINhoGk%804w7bZ_={Kt1 zpMjIUeFtmcB|slHnGDyPYoyggHme2EO4N_Qcg)lZhu(2_1S z#SS=*kq;J4{EbPqt+19KMoC=^Q8SaIabKruy0?`*{Xm&=rS*3+!z{nHguO}+6FFeD zL`_x6Lrs*#XdsoIpVdFa>Mu$TGB=sI<%+X{^3KPzgNj-~1B`Vh}R z!g($0Sk|$oLoVXZT-eE~Hn?<)T*H}>&#IX-S~53&a)O5+KW|+`PqeHGPW~`AF~4`X z1;poHP-A#SI!VlWl+*`96>$*SPu8B&+Sj2Cw;p}?9S{*bcOAxqr{<1VEbFJF~90*nrRBr#&kK_X0~C*WBL5NO@VWLwBXNhS9P@L?kydTT>>wszs2Bc(QyWsWLL<` z?sqZP3eU}+V6^ZRDPNQqx5YMv%ep(8SK^~`70xAhdtMBRQoD{~ltzGh&oCUYpn13W zCE;Hk;#u5a!^&OG%Si7d%^9k`%JDMV&OYgG(&C`93WxDi`D{Frsbl&!C_j5~ImTEC zE=Znq?|tx_b4c*p%A1D(y&-+@dE#TDVwbI9CIwM zW?a_qlBY3l_V;WmfAaahBJrv2>eaH2!AUn0HL z#dU3~oY^mddWI@)_Di76Sb$%0>GwJOfkU%jBLBB8ZuU#W&3*|q`z6rqmq4>$0?mF2 zH2WpcW|cv+Ujog32{ij9(Cn8$vtI&hxi!>IvtI(uehHlG(#?K}_)-@)`z7LLzXY27 z5@_~IpxG~hPrLGFzeL>Zmq4>$0*~Vw(SFT-2{ij9(Cn8$vtI(uehD=DCD81bK(k*0 z=fE#Yj@d7POI_UTmx!DF5@_~IpxG~hX1@fQ{Ss*QOQ6{=fo8u1n*9=J_Di7IFM(#i z1e*O4X!c8>*)M@+zXY275@_~IpxG~hX1@fQ{Ss*QOQ6{=fo8u1n*9=J_Di7IFM(#i z1e*O4X!c8>*)M@+zXWa=k@b(+FA=}h#m#<+__ti#?3ajdb#b#_B5w9epxG~hX1@fQ z{Ss*QOQ5aq3;q``Wu1=~9FF7xFOGDS!-|mn$qtVaaxKhv@iT>#f1|^tE`5!|@wz|A z*E>Y^QF?wUez=P_xwt)V`17b|n#y&z%M<+acE6t7fZS@+j9G)_RzJ?Wkgy%M%GJ5G z&;$qi$SIE(Dw~Q)Wz*=`7i6D0n7CIwj&V^jm=%>`8g#dR#~vnBUV*fK<}*1`S5kJO zxR$u1Y&%J=Br*M+F0bqPLlrb5I7?5EK752q6`tp>G_3Gx0D|*M9&r$y4a9QSjptCf zRGCN8h>u80@2(r&9tzKEmyX%Qb>p=NERC~0vFzJTij7}_C|rRhB{^5iTsM}^<9;a} z+(-_7nY030=DM-)MQgV)n-{Vv*Nx`REoL^0hh_3h<%?9&#cImuQ^Ch-%VVP0;9WOf zLoK;(+(gTY)BJHfsQpa$d)524I3tVIls~|c6pzSawdJqV9mSbhtgiewiXEB7#+0=h zO>tHh+pny*Rq?1SHnuEQw)na%R!Qm(rJ|^rfn7IBIVp18SgWHRfY}uDX098pXLDxr zY)LvhYeK1PuE%oMjcUt|xo#9cA=ix$s%Fk?E>`naxP36id^NeH+qL7)*Ri>$sGVbp z_U^jT)yrHrW|5lmFp12W&B^M=K3q3SSHAeUtloa*rxnYY&EM-x<;>%nXmdoY{D9?WL(8`=JjDSu1*%b87cO4x_%#(T8C2eWz$%ZF)y-^pT&%HL5e zXErrr3a%RqqAb1ZMpekXF&;^mj8>HXK9=pmS1Hz$*$jo3MTOtY_88UJt0dNNA317E zzsoK&xwhrxu2=pil%I>D*851Pk^4NI<~=s>S4e6eisO7hiHvv~Ei%`Q8j9h%Q9|Ie z>&5Zr&KXPf)8lAOeVh6sFq<0Y;kq%aTUY;DWJInTN2%`dF1M-vJQWVi=J(Z+b6xJN z`c=ve%;qBH24>Toh0b?{=hnYXI||I^V=5e6H-1))Jk1qeR6jw{z-(Ti!hzY;of*4s z)K_Hex^b}%B4Re5Rer>5N;#w#*Nunia3W^&XvCQ7MtcARX7dp>ZVx+!a@{ym)%syJ z$Fnzc-S`6)49uno=*V^Bv&!w8*)05wy?SO-t{W>l$g>>FoYZSH9=UEjLBa#G`8E{} zt{b~`1n0Wmo>za1awBF_xq;a{QIh*~-FSogGBBH(04hez=8Xz%%*Nr9ua-JZY=0C7Xy?|+Fh77g{Q;;GZ^-yS|2Yx zjn-$w9(ih9o1_aYZ_!whN$CPT^3v1C{e)8K0_`b1gMIy)Lr5+qt&l9$|IRS;Fjk~* zmx_b4zwV2Z99-?4z9?M&d11t+xist7CnF`YmEq`h(K%IX#CR4F-rDZS z;+z)LH_ng=&5lm^O#e9zK_v^vmQ@*n-D6R@Z0$byNn^FEm#91GLi8N7| zwy@d|_)zD2(U{}EHkXTz)Xiw#oHxu#^o-2R2-WU&cqzTGc~y%Xpyo7m%QDzSubR+< ziEmrq)z!}Gc5V5pbv;(cTizk|(Noabf_!@J;iNI>GjM4%FJCVfGbgRr*ou6asO-FF z%M@z$!bsU6zrwOI*|K_Cmye|mahve+dutHO_{#U#&>JrqzXNz3S*YeE=b=f>3&rN& zcqJK=qWHo56lZ{A;XH>Pl&2sLP2~qFr+GYIhfVY8DD7q+{N@}I{I>GGO+;@!AAFvM zh*5H~NXNlJY93K@ggRceYcW4xhs`8q{!MP~eej#&A7Xju&{4i$gkD}N+rExuT+T~; zk~UoM{j3pbEN?0DG-s*yi7WE*s(H!VkXIq!2fwdsUUF^JK8+*1ymztf>qy1}eaO=g zJZ|45QTv#N&E-||l20PZg&_ZX+Y9K2*t9x~p$ z+OI1ogq`6RQGKJJYgoMD{uayrEH8gvPiYfhm`AmBkskTkoc&k|>%JS16Xg0aP7qZ8 z%bu#q;n5ED3{d*X4viBezS6}vIy6p@^bfiCZ4ULZr26+d)EuSak2y3>khpPzpmBnr zae|<6f}nANpmBnrae|<6f}nANpmBnrae|<6f}nANpmBnrae|<6f}nANpmBnrae|<6 zf}nANpmBnrae|<6f}nANpmBnrae|<6f}nANU@aHEj^8*z&^STRI6=@jL2#)nXPh8$ z;{-wD1VQ5jLE{9$r(JpD1c@6b2)>G&PWv-X5HwB@G)@pSP7wTqD`%V_apMF*;{-wD z1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5j zLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L z;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5j!4qn-^JbhNapMF*;{-wD1i|NB zIeGaP2j|y&J4A8vSM%7)IX%)mr)SRx{yggW5;(lU+egkkYSy4Ryi5Aw@M8WhJ$lpD z-5eh8mz}#tTOS-=@>*RgoPI7sd&f<(ZH5dars9vQNV|`c-YaSMdY(bmq@A9pgD362 zq+JHmPDX%%v^!O?K-xVm$+q5F=}j~;C+)sSvA#*W8`48+mR6Gs>PHc#Ok2ab;Fy9v5A&Fzn7Qz@6BaZtKJkPz7oTzRStm9xNiw^`{J=7j%IrEL+f`%! zWW$NAtWiVF88H}H;5B#qq{wnM*v5IH$_ONJiEXReTh`z(qj`19B^NK_S~5f9ZUp!x~iWFk4b@nYk-cYcHO&<=MAW_&MvZ-K-o0Xjd{tZcCv`lPN>? zw>VT@Zp@#Nmy^FUAN=MV68yIE?joW$oew^*nwJ~PafpN&h?m>O)YDtN5UTjiYF=(^ z#CtIfne$YwQ5`murM$TPq>|z^!9q1JH!_;n3xd+}4cl(oVRm+#Qc_zqpf znqf;>)4l^4fC?BU|Bfq2K@|^EcD;xVK?bi<} zws_7gZ?`QTDguM@LmGJ+9oN%esptE=KhofU^lU~oOnm9o+1qoaZ8NCSY}g@0yaUp2 zkx&{@dM%|gKcvN9X>(XnUb!&SlLs*vKfM?;Gd&!Tt|n5<%=F}htq%vJ#Y}YZGVL-r zApN;2k84m9Fw>J#NpL_~`jRFKGsf>UsujJNo;_R50clYVG@S#|Z?Y)|q?%&v!vU$x z=e*0&n(|Uw>tnU$o9jfx}4q$4oJIbdvHM7qW$C!NX=R0%&d!=wD!)fN&CT@GmK}3IdLyS^4oJ;kXKtqVSv7uz+XpkU zS5vAqO2Px<_YaiW0SBZXmB`#o?|;zhL4Nqq!T3VEN5z$)XNEWEEB;DB_wPEBC^ z{+|j5Gd;AIlgI(-qpEvB;Y&)Lu(tknsvFGoOwTf6{H$2S_`Ol>YBl3RGt;|J(jqgx z`O1&X^q$skA~QYNz{U6gKp>B#|UK@A9upXP@KazLtK!5$m<^JaQ- zK&pGyJ0Lwp2RSq|y#~bci0Io)?*QZQq|lNcl3Hmh9K#Qeuam~76ZEV~&m4QN(z3`| zW3Q%H(g`Y#W!|`jjFZx(N}86cs>VsadXrUOlpOd4gcWlCqB9ix%l9w3P9G;P!oTQi z(#DfFY$)z=MPqv-pM~46$*M<@59<24qiU~9-&we_c+RLgKmVfi!!570NW9-`*ydyr z-o%M}qK28nb~QB7r2s>cMU+35UsjT9zy+OLA7YoH{kRexGE;!ZSebWao#;%rcCJHG z=fyPG2AK_#Hw1I!OlD97353zo#ckcpvtxz?lQUx$PC>B%TAnz|L;PB3-`pKJ6t!Y`!Zu@mc8mOZ_+>l1DvYLAs$W@!f znWW4dDmXLcxM4+`VA{;$%GQew_FK-4D4bVkcl*xpCYoQ2)|l>g_#*X1FK$J<+TA(p z;a)U0GYkEZlYp{3>=EI5&dzmbO#k+>{;y81omS5bx3+M^8L7CUW%cS7BTd;#ntK?k z!SlcDZbjYw98;04b_I)noYS#Fy^yn$t6O!6`>T+PovV@y-zQ`?d@_C0wI9)?&Yy5b zU*@)!oGUG%*PI&z2BNfYC!(^@Y~Afkbk|&Dxi@Ywek1tz1QbvYPN<0&RfROYlX&&# z98ZX?#d{^Z_cSI*T8E|Z$lq^xoU235yzn~ zpnxRZs3@PIxY9KKOpF<_51qUWG!E*B>|d z9P(c4g@Jxw)!g81QTwQpx38KT91rdLCWz|0Y zZgB`$_~RhC!Rgpk9Ox^7lBGcP{F+kybjp228!q^A!fYgzWn2d5C)+C7?8#h1P1SMf zSp7B1X7V&#vaU5BygVQG7^nX^xIuAuvwUOd9bG%eL~-RWi{kkv8!rWyXnY~E)dq2i zqU3#0UW1Q-icjXJvKr^A{7ldXla&u6F$k19(QJ^5D^ZOu<@r4t7=>Yn-=aXq&5wl> zP(Ky8fR}({xenAXikpoPz8`g;^tsj?2A=kf3m&R+V3Wh69qM{f`pFK@cev8wMu(dA zRXLlF0&jEiyByx@@cRzUmXLC0O9=kPrJF4w@qM`nCC?_!K(i$T&6W_<^{#xgB?Qfu z5Hwpt&}<38FS&B}IW${B(#@6-G+RROd6#dtgv8C35Il|>P5UuhLeOjp!8Vs}wuHoQ zb#b#LByP5ZpxF|FW=jZ4M_uyFmJpoj;$}-o+-wQKr7qoU35lC6A!xRQpxF|FW=jY@ z?aG@iA#s~u2hElcG+RQ@YzaZLB?O=3VWIYzEg@*OgrM0Hf@Vtynk^w{wuGSB5`tz+ z2%0S+Xtso)*%E?gO9+}RA!xRQpxF|FW=jZ~Eg@*OgrM0Hf@Vtynk^w{wuGSB5`tz+ z2%0S+Xtso)*%E@rU4mvy2%0S+Xtso)*%E?gO9+}RA!xRQpxF|FW=jZ~Eg@*OgrM0H zf@Vtynk^w{wuGSB5`tz+2%0S+Xtso)*%E?gO9+}RA!xRQpxF|FW=jY@#S2;I>%WCu zb6Sx@sE57M_jI_wL-Q+2x?CEm+$@K$cloC{9Ixx2e7y_B`7*SyxIGVu&v0>Xd+1Tm zGu89YF3)}rru%u>12^|0`s!rg`hxPwBgB_TX~$0RM~Q} zXiI6)M~>k6{kpX2$4|VlsQ5T%d~4ct-xL4)!VCNR`Xi9@+}<~Dvwb4#Z0Zjqtro8J znbR_^)t7yaH60&mfU9k~fk)mMTUn=XQ+cQ-!4JIZzM*j%W4M~{7~Pllx%e>Io=OrlK*`$&_YJdxDhu&vbe5o9ge zT0H)}zv8mtwS3<;9v|$3eD(M2jgRvf8kscRN4tJx=im?Q^H|!bzU!|q@2+HhQ>w5J z`lFD(_gA;q-Mp<>$?QtC-%lY+ef*9uSwtUH-nT8;q~l$@mAsLqrhnX)Zn~Ot zcE(oD7wy}$RsF1UQB(Ks#}oR>pEL31I+kOcJaOjSz1#MvWY^8ad_NnuZ9zai9peeE$e8lw&mOhQKFYeb-+5m)*n{ydR?02yv2(d&6S zvy!O%{K4Ss-S1Ztoq?!mCDE@Fbt{P;ug%{5?Uu4yrRfNYh`>H*{Nf#4h?GU;Gd4xN|zkW&U>85#0eI|Np3IIg$T%^f@aR%sauk$<-Km;2kjOYQ7~$RDP>oXG!%>g7a! zxAvYB`Hw30=r`?%SV{B^YUqGOK8~+-%kEVg2kv8ZRs$0LoJ9UV(r_d4<#{_=N%Thb zL?H67jH5O6rnKR6YwO>wx`D{IQD<05bgAkFB3}wag+SyFS2V07daI&=$d`M9XeH6l z>5K#--zeSSZ+CCi4Me^)mLekmF4YZ0{=>=*M859KcqP%*I|uUh{%`AQX}&10T77%OSStzrgy$p4AN1S0<=HM(yi zzjV2ZM?}877(^?H9;#?C-8Ei65c&2k8i@QoBr6d4|E{`$$T#h*K;+x^a8J|Sh{!h$ zj6mdnNTMPl|3>X8BJ$5renjMN)~Shz{QGq(BO?F%%8!Wrir|1Cwc28j!du-q@Ch~Q!dLsX8I_Q^($S)mF52eEoxSpYPDy@ZdX4*ho z6h5rz`D10kn@*j?$SFN%>>8y_wa3}nWA~?!i<~v~aLZBM^vrSptU5<3GK>RZ67_^6 zon@xJOU6krFtt~5`lfL+IkqfPcyCEGL2_Ul%u;%W2|D+{MM=8E+IFTC5+tFbuYmMb zs&`GQCTdvbn|YNrb4k|BtE`!4O2)M&V{x{-_eeIoOB!T0pH?g{9rsmI(}Q0?de-K> zO3FyOIg~>3D*f*?*TK8snx@%==8MrfI3itsVbg-f1(O@^K5gU83yV{ipVC-7VR>=% z@{P|P-c;P@imxvGNzDa?iKBmdO3kswbLJQKtT_h-1q_g0qU3yL7>f*agR}FjD@OO^5vS#BR^F}p68CX!6 zwQS8DRX2CkewmuXMy?*_4BY5`IOBYM@`Y*L_jBT*VEt5y$&oxn5vc5f{~}QIPJVit zsn8`^K_w@B`y_T+1k2ZC=E9yI4axvR@Fbg4?H9Kh2kR-=YEHHfUOOz@szn*sHZ#lJ z(IqZ+b!)6>uoF}>n!C8xsuqP5Gc3A3G)^qDob$FXs~AmgX0Xj`u~=+gZ%n!|=z-pc zibt)Y2M@oK%&=_{*LFP3Zl};e*F;9W-SSoJ{3Scu-m~RZ6o0-jtPap<@x<#RRH+wa z)OMyH;T1%zk9wAqW=W>(k!e_Dsv^DVA!v`i`a90Ac())g&64Ni{4&^1-NWm=aOTYU zvDa)d3U**OE+W?e0#dENgFFzoNONwY97LqF6biW6ko;IPMl+?bNL^ zg)=dA%8WRBd6%@Pri6uB*RF5Xy~Z9wS__Zx6-vr>+B3G3%&e2%bSdKZkbgT>>rzaF z$i9d7k*4VNno>Dldd4RzZsRm?HXB|B(R}-0wlyB|;R)oazT~N{m)FR)(!D(KWxdH0 zPoe$#pmH*%@WF4MM1tQ|-ft16H?|KxZw?7@`<|wKQioOhM)33PJBw{!ht1?!6bdem z68z?3@?v@GCWPZvx|i3=w(3mdr?3j5MaS=h$|--nL!Z8ma_9I6sB#K#;TP}sGWJ^` zO~;{GNx$EBvhCZ-X7VudnzZ49&%1(nEbqh7am2b5;)8u1iFh7)6>YfS^Yld&%lkO; zC~EB!`u2UAZC{7YWGqS(;;MY`d3TYZZQu48lzY~9hRaj_y>1(@Flpad_aN-+A##h^ zkk863r5u++9{hf*bvWKexqr<|1$io8h|=eBW6dT($D(thrs}xWC+s&58zJmJn#Y2? zD9JW`dlcDm9gbl=#1&1OAS4y-%8%oF*fpdSwL7K79Cj3niTwM7P%XclaJr+ATK%i? z4oWRwW9}?u`89dR#7C?C(iI&-(X@NwxOz5(qG`>`$Fl37qbk0xD*m>rxb9;gB=1@u0TsW2pZ|tYdjo;n z77fr*PH`jDY_;2b#1=+$8q8~nf(!8*r4g&tan~$UBBN=`T))O=x2?wa3`()9NKp1_ zzeUwG%3SwW4NqFzI@hAJ91Uf%`(i8?k-o1qLoi#&J)>ux;+G5gn9^~M>E%#Q&tcWS&7q!;ir?$-`wky-xXq#Iu;>PTNrk4YnUJht_IUr=S?Z?Ol;-;4anqCfQdO4u!<$$J_1DakA z*hU|!ou-!q-s<9}mqXn2azN9|0gWvHHP%r*)5`%(F9$Te9MJS~z@@I7>E#f&c}URo zazJr&l4E)~;L|Q{dO5^RF9+02lggQ14rp_spy}m+rk4Z$!Id+;9O9;z1DakAXnHx| z9Ih$JGrb(p^m0Jc%K=R<2QE(c?mjjw!4rqEgpy}m+rk4YnUJht_IiTs~fTouNnqCfQdO4u!<$$J_1DakAXnHxI z>E(c?mjjw!4rqEgpy}m+rk4YnUJht_IiTs~fTouNnqCfQdO4u!<$$J_1DakAXnHxI z`S=F^gF%|kgZcR8+S3bM_xA`P>0=x=IFt_{rE852;TwdUb2IlQzRaa}33)Aj*u`%Z zBKK1c?{Mi~b@)Svk30OG!#@hSChh*EKD$T?JcR7IL;M&QpDiSA&nx~s`sWqT={K)l z>$QC7F`s+xZ+^4cvZ~H;IYy7U zyYTNhmHZ!=)z(dw)f?&>YRW2`0rTj(SfL8Rud^`*`I+PTgGy6=yw)FFT9$$$lI#AL zWP{!c`lpRAwEiu8S$W`xNxU5^x)aq{bzvDRqzp?fSSFr6GnJZs(X8n+rlSDU8yXwb zu8`B~=FFQjy{URSrmd}8r>}2lWdS399lHz?s>`h8<@1`8RwQSZ&X`kr>7t6SZ1^v2*|uqOWfdsaEQD?_woWGuW$h?t&6o9@ z8^;o<3A37$cQ<1)IBqM0UywO~U%TD() zq|1Ja>2gYM(#@Wm5k|Wl{>GSeNmV;lJ_jfOj-1!>B1`#9)7)EFodc+}N?Nor@(gdN z?cVd;%+3kZ^BvZ6WL)PAFwBoKuK2TGH$43(8#bfnDUWw1GJZx)(qA6kFn}+m4_WvR zmNTO0!ZbOTI9J>GPLa9A0QPSajmA8>J6X{ug<)F>-&eTl`Q$NfXT8u}-gBUQWv;d-z3+p)Cu92t0_5upC$Ih-hV$GHNH!+j1;Stn=w zijAhLR}(9e}<=Za>lV_)jm4a=zqeK zGx#`me{u$Q6zJyUjLYeD2u{v;iRpB6a>gyp{A^X?{P;7dyy)bNE#&4ZzY*~-P)c}m zMlreY{$I~A(mHIWO|@2HG5&?jfqGoKmicYe5!&Uctz zHz#KlF=wIrU7FnYpOB|*6_#cY(Wu|C(RFij2B-I#>UU9K>V*`M?d3i?I%u?yPAIA` z`{-PW9LdQUa{fSc-2JJnC8J;$$krwy+v((tb6IWO9(SLhsOaR3Y6gzf?|#gRj84vI zp>L#qw~W5gK02f18PglRbUN;SF9S#FcLzW2{yYYb)bIFY9_-|db6E&W{qA?Dwq6`} zKa<6=)bAERjGUYy9YCahH;Tze$K6wr&_9U!-2|jA$K8vTb)44o5O1xQ&!`a>jl}kJRr@r0_`njvATWpPa!xwz@w#V;Lo9s^9t1J~~`nwvM|$ zgQ6_;yIIVKrGCdXG5@`Ma)xNtN9uPw*@$~kzbi%+WT@ZqxO+aU=Hv`H@2P+4ce>-~ zejgok-2Fy24|UxAIE;DSuY-3NxDp@vn}`c4ZangSzW7(5eZ|7Z)&R3J2%-qc#Z*%_#V?W4?wTOR#mD2Dw59K><> z3=O^N%*V;r;_8p87Z$(Ytur+2S3scO1zgwWnx-*4<2we1YiAe=gzKo2G>_4Y7-I0Y zVOW}}xEQ~ua4Cc_abOippP!x?W2_6{VC?k!7chM58DsoTfP>}iOhY-Cd6t9ge=INa zwH%j;1FQ2Y)(y8?4#s*q{4(UBqD;Mz8hKT)n>esKtML+_Op7+Ap4Wi4U7CW3X~07KJi~yRJlzXB+Deo0`W(VRbt5z;T=qKt({Oks3>C>^c#L>M zb0C3wh9k&9&$g!m*2FMYB+qgZtH}?Up4aisQ=p`DUPsSfXJ<=|jE_6Yx{r*VI_r%s zCo+B4fnu#jhB{Hkg>}Jod=4dzVS6R;8I2mSG9U4@%IWrO(}&N}(0VU;K1A;YRP4sJ%@MDRHL8OnZz;dK8IDwy<$%gyWkSsK4%r-UxMT*zeTnie?+J~;gs z*WHD^Ire;FKi=H3FO0hb=V`3PzA{bN6Xc`VX9ch4d8s@04{}!QT>$&%PlUg7b_e#I zxyh~AwTpYiI8J*J`bpWtgL`#6ntFNf4yULisMx)KB(7rk?MgibS!Z`VntFOK#!~3G zS9Qz~dUnT9_&do@><_T_O5u*(`;W(7B@XC_rrrA~D+XD3lMMDj^HOat_J1jgrLac~ zbYl*DQ0+P6LiU^3FNEPK+fD6~w4BuX;W5M+iSYYn?~+gKFD$%8_CjG;=81V@zJ8+P zFD%@>&qI7-m4mP^$%rKL@$}vp%2HT}baAyqmN`4*VUc@IhkI$8&;4=2eO6GH%(v8s z&d-ww>&Bz~UAT{r^U-$3?f4zi|Eb85o7BbrCibQ#L`BUhBoRP*cz=_F>~CUWqGL0+ zJIUVPWU$`uguCk}^>!xFxg^{8fXh8ncq*EtmGw{UOB>es^ z$nm~{Ah~(mI|3{0@W}D+haDT7+=gg?Rv9JtL--4V33(+593)qO%e$X}i|OISZouEI zxCYSz#TrAF0H~iU( z%a4B;MfB$=t}uQIxw(oP5f=z?p5jKv_tWoK#f^!7m{N{YT+%7zwlU~wpit*8jE`n| zi|E8U1>^W9lk-bC3jJqnBF7zi{G>Ye8%ed-%=^0z%@DkV=B+{z#-F* ztMrbKKgaZ*d{E-ek6#MvKlN?l78c;MT{L|qWl{Y5Oz*oY-m>@!Oz%0xRmA^;+zYet z68B10#!H#&?>{ePSmpTt&VuF)CvxU8LJ><>M7D@5>RYhN=OLfa>STBfZ_Er#)&wSA zI)X8V24~@aIBURt1!*X`V$*VW(4Tb@rHfc?aEP&tVZVVE`D2^MG?8Vw5jhfQ^%)j@ zuBS@tB+fxtV5y?gToNP}+iWaxG3&zk<|nEd_XHicFoE~HqJI4|yk}3jLDyoJ2eM?cRTi{~I~D1+49y|7CpN?EpDq z2g05SM}bzQ1BkhF!_%4QVCDwNEyPY#i61fAU{}?C48oYs(BN47AMqXhO+jh`t-j96 zEOF^ZG150p)NF2IFf%>L{WtN=cVe{gl3v4e{NEr|K&yX+dfZEdR=K4FpjEz;DD8I? zevYoOAW^_ft@0S!D~TmUvE^KkT{#}r#8oW8dAhbwP5cW*o$nP$^;RTaq3>$%I?2hZ z#MzX5f%k+Yv>}nt%3Gt^ro_j|rZu}Yv4*17dI_o89d3d{XF#iygulj3@G}FyN>Y|1 zO3q(aVFp6yJSR5W=8traya4#`g`Y9@L)D5_ymn+>xDa< z;qnu4{|^3Rzri2dmT4D{A%L6f9fwSLGwE^E28@8*&mc{AgEUf#;3!B#tJUJaatt>X zaI2*kTs8JpWY?``YVO%%f62hr#bnMI*NhLK+&b|&cYFih6z&}(;EzlZ+)Ij*(s-Zw zcU|WR_;}75{vXC^r;CF7tkUw*@-po0U*Eb8jnT{_2L`15IX0%L-j%|>B6m&ohm@Xn zYFSw-H7zyEFJ9sgT7o@F%I2)UvdrJGauFknC_sYE8F-#J9=V>ly z^TTCU+~|fGs2P-#6+=nJ^bGoP!wH_r(0Tiie>m*d+H5;rTVJ`p1^aKeq^q}9Vk41t zU=*l{5q@~khS^4;51V!zLL4{~zU9UtecG(!&|tXltsOnyA#~1_ZtgpiaqLiZyhwFx zb8}6@WXk zZ)s@M2fdh6O*S>6*HgP^)ZtrPbw&!$1%ln$2;{xL1BmI)-g7&Fh;l@~b^!^$NP3T9 z!S zYK*ZifP=Bqk25E}35_v+C&0mSc2=VtlaYCrgJVEbj%+L#3!2U|MhL*-nYc8^o_ zKzXbWBd^Me1FLgCz`Jr?RMeIzNLv1PzVx%f^E5 zu=2-ohLM+z1#|ECWGF34YZu(?{M~Kk4_OH1Wn;k;Aa9Q;B*HO&yzbHzM9jXT-eJL` zQ6XKGml&mKdlWbi;T{pm>W^vCkgD;Lu+BOF5L}rf7R>r)R!Lb_Ti(l~9EbPZ7$eW{ zhT86j0t=P}HK`FxD81W8%8yShV_^XIaRNdv-ngNKR1hCn8lmn zEnc$#n$@8x0x;Ls*0;7m*^JwN$(9o>bq#f!9ksPY#z`vXwTn=Wvl18gj`=g`aE!50 z4xXp7iWF_=GEG^h9vaCVq}bGieMXP_H{ioV(aQv#!0@2cHGRLPr)YYbrpyn+ZP1j@ zJNX+msGwX4q?`kq%09o~^YHM|{RmAbX}Va`<(jV5v_Vtu!@+o8*7QM5 zpVssznsVO_hLi6&KquiLCV!HqXJ~qXrgBy*{I_YIYxWHPxTY^?`U_3}psDQdjBrKh zFc^MaqOw%_tWt*Y@Fin%1mT7u|rl)DTTGNf1UZUxTHN9Qa&ujXirq5{l zvZfuHzNx8?2F!dEXnqH*oO`3i}(=TZHkfyI_`nsln&@`7TV@Nll z=|oLuX}UnuGc{eSX``ke()0#RKc(rHHGNFe7d8E&;Mj;a94`hgt{tK298$P1)U;Z=H)@_h1;e8&ad=8Vk9c=2M*U6OxP_u%-aX;n zGy8yd--CM(6XM+o^w{2vJssXKcc1I*+RMEZy8Px;e`xAk=&Fsn<{SxpF&@WJ2uL7z zh&nR?$RM6}9ZZ;5se59+I#1!~{OFK{K|mcX5OQ#IssgkI;>gc%RAGK}d;&^vBr8>w z1vd3!5f*#FhPcsIvKf1& zM#)A2viF)5A3CmUY^}m-KwS;SEw%NHm7CKo8!DTy=(eqiGx%17nKctjaB4r_jC4Bh z(%QuePk-*5x$R|{_X}LBv!$_log*2IzEP0fb5+_blH^O5OT3&9E?jtAaxyA-N>UbP zlasNpWev`!osyiq5K9#+8e5yIYnrzuv7JZKY)Uf4If=t3y@Lz76DJ4Wb5|}pZLyQ< zVJE7->=V-6z8~G})cIbRqjT=fc8^!5m~#Bv?bWp6Vz@ILh9+VbX=(`lXpY8(*IAF@ zrXh@p1FJ)MpQS|`<97mhW2e_IaPi4A##oPo173%r$;W$;<59VzpezUT#rc(q11k=b zP#O@l(-^-cu-o$f40()Cd3eo-^47p^;=t;(Ltc^=ZA|^02i}$!pl@Y8Qy$$-{y3+$ z<*_1@a5KjEZG?j@Zv^C-{IT0M@^-;);!sAi8+qH|XUiL7txu#=`iSju(Ktz*q{{h1FX70^>QXVvsK#e?7>D09!$5`~_*a*u6 zQV!NT%ZhYEa{vzPyNxmO3~#9I-t*ks&I!}AQzXy36q(NZpX5oNqY!rLjBnpD^1Snr z@nu&Un`aF~#?N@t8Gjf?Ys#>WWIvV6-Wl`Z=*7c6qsRFvUZ65R1)ak1plmy&oQIS0 z87Dni(`A~j(3H;({VO%ypedgfx^wJA%ApVGZcRU;>7AN>QPYPseMZxlNO7xP)-(sz zOn#821*DMsy!MxIHmbfC<816tS+}`HYnJvn&Ym-4PEt(I`=(GV&Kdk9|PyjB!U0G3>o-@ufb8@4g=>& zjCWiy7~WDijdjV;@L6{+$igm2tJ45C9MmS= z>1DfQSc|6H?Ux*Dh2tqTJ~G}R4{ws#&KW{E|!BmW%f9g-wM6}cVmpaC2+9i z#n1&aKII*3oXWjYk_cmrslW5!V9P6nyh6B99^Fm;vd5|Qkk{3?jh(zLFKOj3ZpqsP zyD1Z_&b3+jGk)3Q)X7%<4mM7`7xK9NYz(cuvd5|1Bc(Uv)M2QIuF5+MGELiKT4e~= zn{jGNUXMMGQ&Xr+jysI8ie#LG+U|#9oXWb7jGa2`!)^iOdFLb3pK&VNBn|rjGfoYA zaEwyTTj}p*jQUR~2gjJ7CPiEQoTiUy+LtkBbIsOpx7?n_oU_VPy*?YeoE-|ffN~+~ zTPE)btnJRgcQnb%_BqM6S!(RLdas+@`4rDchFeD=&M3yWLwlWeoKZXH{+Bjytm*4J;OtyD03}xQ;{+;dHC%)M6q13{C-lQix9DGM~V);&lZQEO@&m(?! z*OA?#-mD~Ne$Eol91G{iv1qLpmxn})%N!*am__HIkvlrtp;(RPn)a~Bbqqoef1g8F z=W;w(Xd5tUXnmR3)t}4n9?a0XfkATo#UL13f0kUHA#Z@gDEe#4@&AEfXkCcNaD19v zxF)>?90t?>#gKu|B*C2n4nyk%Q@CGl*`kca+&4ny|kYvE7DHhhsd1o zwt>WIn5g`X50|J}B(+36T8+yGSJP#*1X~&3fI)ilTDq)>bHJ|btMkhtim&Pvt|70{ zznFP(S2+1+z>9C~m7&+s+N+=*PEELm{u}<>>2$_T2#WDPYyBQHf{^x)32;mnOQ z1+3@14ch=s3pQ15-Y`Jx-u)Z@G6Q9$rDh49i|0VM(SLNBScEI*cwECtMzNdv(J`~8 zPCJ8#;LVs;HUngOO6@@1X)T?7m@&N18wz4KxVt^U^F8sn0`>bMY?6gqaTmd2U0)vEKMSW%Z{@b&#&3y-E7@1Rv!O~KrcL%zh$lc_pw$NhJ> z%--dhS9UWtjhpivd$jZlhDsb7^4Y<|5stqnX8-}Hsii`1k3dfGq#DP`h z{SPhL7{3MJjh$Y1f#FkYjIo{o2fG~YD91c_vK$iely-&Rn0-jc0 zcWDYDrWiM&t9j17@Wm}?(!rc_5pJAeK#l0^dCnGu&O_^6JLik>^s7FkA7ie+LKNTl9{f>CTQVX^i+f$13@g!-@|%6+cf7wY>T9v zKa)PC>C2k_Cn*|Nho-;R?&EM<=srQyqe&6}Sj~Sx(*@eSLh~y%Jy*N)IbuA12Ss`8 z8@Qi)kL&(CmMU8I@A15nna9p4Pm1Yz-9KM7$#4!v{hDC@^!+lg!W_$UpYHJ79sAte z9sAmbVy@)qeHwimOYjkqJ}$$=TBJq>UG zSqXyK&zH#M8M2>&V?MhoIKByj*$;spI6hA<&;I}jfQ1`K0-ucxv!A~xt_lRRA3=oF z?B@=+=-JP+4C5!<-1-)Lo?Rp}9(-t+yNT=w%?3{1FD2X&ZtU~0VCu#EvM+ZKw~cfV zXI}mhE(H#jcouTOV!Ic_iCi#ZZpF>PnKuXdoEZsxF@&8OTCG#S{@oqV)N)tc9IU*V z9`fuskHR)UGnfI^-7$|&_tOu#_gZmhjGJ1z%L|~o!vfoSJA77+VU1xA)7^|o-C@;; zP6qqG;MB7Ui|Sl;3lpAyjBHF(W%K41Ie=zipHxiNZ-7oq1Z!l#7LhYsuYFRq_|Q?* zc9g1%t+%6%?6$mhdM-tIT*_pz9fyn4vvn2hr1Zsc(; zV9Q$zd0ovJHtKjFVlK1t2Wu#A7won??#+;t80ydXZ3k=TZ?}~{Odmpd**ZL*guJyz z2*T0I>yw&-i1`Z&$ajh{Cd%jF01@GwVK#17Z{`du5RPLzV~jjf)eS43l`Iq-*{2_D z&ae{g

o;{*64#sgrxpa|SynEGJ58nV#nmc|FV->^nyJyz`Oi4>n@v3=<(n%@Lw5 z7ITE~h4=S1ANT>v!}0h}HT{*Q>?7#zx=^Ljg;Z&#g5#&~{vIre{>y=$N6?g8E*dOY})rp%-i| z*r&f2y6LQkXl(loxLa|F_*z)H9@{dKF^6CsJsi;Lk!$~kje=NMRhKcE)&-W7GCaCa zTJQI8_wX>a@59u8RQ;X#czy4|Gt|=I3y= zj7t5T-i%R4TKPNJ81=)DmsCZRe^cJ2Dv-@(SH+!>A^M;Svauk11E%NU;ZW{jGT z_ik^-sK+2&Z^o#TdDD=gp2nz?(OwRZQHOcdP7BL4(e)ggqK-UP3O<*F=vs ziLk*jbFBGs$_U4r9KH5ttjUqBJH{;rp^3(tM_OaevDd=u?_;dVcGdk@bNo9UYeM{c zW~^D)BDu*Pa%PMPdo@aYr-M{g=ZD9dt?ScGtyK+`o1ph?9iVQGMAs+T49F=ohsTzX|GA_YBg zo<;9K2i&dD=QSkT_KKc3xQHz0w*a*l&`&nax%OLt7`u!}P6IJ-Wj^}i+D{I2C- z2(IKf6&(lOep~DlQf<&9-?9G9x85}NtA_Fi=Q{g(Xt3P)p zs2R?FfkATo8$qC{@Hn|VL;ea*D1T8t&Eo^s@xdPa{P;>(<2ZB zoJ@>!1D8sC?x;||nptr#$YBoM5i*KhnTsX>hNE2fEbLMQ>N%_VKY&Z7TFxGMfmhZ1E!a2=mlV1m zgWC6@@9UlKY$Rw3FN4m`BhSdWTQvZ^YTDmDI&|IiaU^=^*E;_(RDQ>!HR3@OmS!q0hVLmH(=c&hb@t%8 zfEI0x-vaQ)POq_G__P^g{7!&_T@J1j?tvT2!S|{u$8y+B99W%K!CX#@HpcIC@V2~H zAdlsuJW?aC3U(6*R%aDDglV*BW9oSgcw1gOO_Y0KhgXyFN+O|ghOxqB>-(O7aF28mj&hNu&}_iXnh(bWTsRJ6Iau#3t7)UK zI`_cg1GH#k@@IHMZTFtBdpiZD$J8V9_n_}>dN{mtl^~7YP)AGlY3WAhMhXZ6{N$Jt`FUtwEJbG04?~e zzn9l*S&v+&mGxRQ)PE)q`f2T6r+M}Zz0?iBHrSzRK#%LSvu5}H?6}LQq=ZMNT*zb` z6*@x3(0ktBw)gFnzi&IfjXc6x(II2%cKvOobHy`WvGXKu&2w*$!1t8!_grV6mpEx( zL6T$b-T#8F<&iUIVN6}|BF5F@_7-4V&2hF@T(Hk+-%Z`h!4A$`Gvja%{`R=Lw`1r2 z?cetfzq^F)Yotv~yo+5Mn<;wAC$_Q7fF9L^wJhfObtUm<=+&OTB z0`fVGrxcKHV5Eb5e)$^w!_O}{V=$jzeujyDobsX_0$C(`hrl)J^UF~Zc4fRSrFRC* zztGs+75R0Jo4l0~R}A6DmPuIIUC0NTv*}SVQ9G#*E$2N67Twi41g250lf<=f8yq?4 zSB@!hEZ}Tp9WD`hjEJPuT3i|5ZaTw)R=5ps#Ch0m(ZdiGIL?j}i|8oZfq9MqG6OTQ z8DQ1t+O+f}ofhcB4=<&tYh%)<@D(@=lI)7=bSQa+Uzoa z@RF|2`>^LzyxEvftu*r~`lhYkpo(B*fVcDpxEL-ssw6~-7bwDSZmKDP`T%m_NL#LwVWrxQ{}>7%hv$ zAAYv}S&6;NzP!omi1GlQlc-@L{W{yEO6At6YjQQEu5$;(N$qL8t>b;;m zxesL$j?{7CeL;4+obQ#Ra*I_I`8VlWPMzEj#e9!CO_9mW)w}V1XOtG}yGYJfFm9|r z_A4~IaTz@sJxs8tb1kh^(aX9XVaFgcpVuDeVrZXZ84h%crYTM5YRW!={wHg?Ow$#b z^1BH7S8BRJQ$AaCzgSbQ^N?p*NI#+}*EGoAtLcN9KBei)n*Jv#8e4~^zt`?u|DZed z!$^9xZ6Ul0Y;qi=&?dK71k=U{(sItD>4TyxIF zpZ^N}qM1@XILwXy0D_qkx26eaO1bWD;pz@@cYx3^SMCLQzaojP<$pyCpH2?m0juLQ z;~G%#L(dUR8^{DdhPX@6K3wFt6YgPG9_>d;#_;Dnjq6ri8no z{3>j=^Ef}lr3;nr)`r%Wnsusb`Z#hv^yB4!h<#1?eq#G&pGU)apxyXX_tg2@!}$zs z8OoqJ8W+cf7^8@puc;v>+%z~CJN@S4+DMBw#*gnVW2e^_aq$T<#`qlv2YcN1GBU9T zZdlSh=(mTB5Wx4FF-G1JIN0)T0mE`p9+&l4FXTC%HF02dUI&w;MH^Fpe8<}I?f}F0 z3+2(>$m2WLmKQ^hY{n4q3jJ7Dw!HhmnEb`zXXNd&;=t;ZKwgp-ZA@C(x;773`NJzS zl=m^%8HZyGnuU<}J|hG?t-L;|DTtUlG>ERov!8=6yqa_{=Poe48PD?U9gZ`MFX*?zMQ}B)xw~?&^+QxaGiYb@Il2`Oe2) z$D<+pHk!RhjQpUTdpJJbF}ta~V|(yo$F$?FZXcSvrz1D1aE7-}T)6M;Njo|`?(O)# zozTg-X1_Nd`9gl$p_?NGkiUwB{lXdsWZ#mmafs}lOEL$<+~v_aX9#$MfgY@V9tlbm zhL}U#CG;@djK7N@p#a;gU>it64I~G_`dM8^e3V@IzEvy8vyB6KSP24+ zqWj6^8S*9r$NX<8M-DqNwkDtt$A5)mwD-yF;4p;#HA4peFa!Zt1rE@I?5d-nhdUWb zK@ZGOpY}ev7ZJML`{W9G=)F%q&&+WW;I5TjQs5=CFSY;4`3+!m&$>A{8~dNo?w&6Z zRt@21D3#aJWz7(76RGU0ht!Amih^c{<%&=e*U;A)w(6Jo*ZZkB&@loBGnB6#cZ2xp zZB&Mhr2A!bZxDBEz_Og%s|*i{;aIefKC7O90QGb_6$?i&f6i<;dCp&98%W)%{*1P( z&9UX-xk>$zvI()X_K)n-_K`nx+*7B{nzC%h$urCR_)h=gon=>)l`URcRyG6Yo1co~ z&Zni&|rP_jH@3e z;rT}y#_)Zy85eKx4qV3E0?Qq^_*60cJ-F_})rRYCT*e&QZDM41%5VbK!mVZwzG2J& z>=c8|FlU!LHCvF@!KYcO(j|8qfRVGBBADVGv^s1Yqc=V~^mDhrxsuwUz&hCR+0R8x zH}Gt%!|Zj)HF!L1GAd=*NtzEwt@N~vVW+~4;d=_l@t=t!V!-5C4#v75D;&=QWiMci zv2y&#alA3yW07b(%fS~bI&{g`a!?ZoR_8@z^jOQmSWkzaEpKgsicfhgpOIGuyNLrU zUewWih&;p5tiff=+W>imaHG7zxQsl$^KE(aAkXd}jb9_e+48np`D6cTnoaqh?#_e zDBG$rCdxx_z^!UB?iV23IKzONaM@VLr3m+I7vUHlO(`_5VWOOZ;}LM74Avj-59=gs z%dqF8T}=cv#-wX_Lv8n-=Z1C)OpmEYrt@5oEjlvX%aZf_$f$Fj_k<31ZYZBEE;0kL zw`C-a@{6PnjT`IH+$-J-HJ1!~{E2cdv(aW-7w&c`dIr=>V`+PwW1^AD920a3!-KNz zk;)tslzjyGlQmtY=?YC{jtT!t&2P|@&l=-htm);N?$-1pn)2CTxF2ZxQ%!%R>7O-a zKgT(!tcZikC>-%b5eWSG+J7x6+_#XT-SKQ`@;7S!7R`TIQzMVFMovsPvSm6_fA}+$ z_k-@hGL)8JFVD1^HLv$)T1h8~$EX}>nYk>_0&twwa)!WtQ`^Eb1kg1M<{a0YH-M+% z6AtF5)LAp<1MqwS*mz+dFoGo`L!2lc2t{Qsj>WY2u8+XM%#``7aq(O3L`(JUM0pvZ$?^U*!zxb3l`>CgP#&c-09pBbbk=ljxw^m}VjIhYltGSRjXvegs|HU~j+$TGCijG~fP|=s*4!Iqp zk~{Z7b_Zndoi#$`{k!mc?zf9C>eqbO-mCMVXXkc3=k5OVT(%gTpKB($)V$M^SJAwS zSUK^46J2W_%=vAhp5H#h<|wya&2PCyurcmyu$eDr>R^vl>)OcW`I>NFS4?HUq#T!0 zWRA~6#^Lxl93#M}860M`$G{Ks+e(lSFbX&?)T(tiGE$am-6!B?PBxf?prL9Vznd|t zb-Uo<^3~uT6MlKaA}Q6nNv#Nq!*?4TcTzE}D+h0M+^N^nx?IlTRrWQ5Igg!oE8SPe zSvo$2d{XoOjc%n+@_&UBr-BAM9!^}8*IeVp_vh_!z8lvNb~5e>^invpayVHWoT1h9 z9e*<-@Kv&`khvUs7FaiF9A^#X9^P)T@NAAlSCgM0{MrSdMu!95lP?4YXofrBN_M*Y z>YbWhshfgMUHP@IOAuQ)dd>|MuhHH}M&4_uC^|Zh% ze0Zk9&hR~jOIdMXbtsP=vN1;d0yr2u{f@%p%qQI#W91mro=2@kIeMdW_cLTvy)KnR zBk%Mqc@JUy$8xb=NKL&|!EWNf>hMg_$+T!=>Uj-#JAZp1k8dK%!#g~bmpu5#|qt}#a5-Eg3t_nD>^ z;W&;qhE`r*(bTx_LxW}eGsZ-D2oAVaO~!q6FXy52dN~ih9TT|cJFtc`QHCxZv^7NIlyN9;vGMCF?Su0 zZ^r)g_S>)iH|CtD1wgglLAnjN-Z5_mSsBeGy;_+MCg5A%&OU{3<%nfGFO z2Oh~V&!`I@-L&M@gPr+h8GP7TZ@tO8$tUuc^E&Pu^4o#?_sjg$o%@|VH}7Yi=d1Gk z5>3?!+NbXyNxydZrEouxcYf>VBV5q_$)7=<3db;EF$oX!$HM3>adJ86@vI+;!)UnD0V*4a?EELgb5KZ z$CMUJFGL5D&~$C$4a$#iv)AHSZ_bjZSj^j624cCyGA;S3Ghkyear zAk43n2+gh?{1*{xqBkRc4)W*CROvWzdE?AdTo7MNzjDRJ;`cM1*^0}L*D?=t6jvDk z82#odZbV$(NAnanGJXm}9;>)9aqgF(R;Rp@q*FKrNxY0J=6Va`chPSVomi)LFy6`J z{E{7R+^6#grP@n&c=1=6;u6JTkAv8;@P6KualBKw13b(9QP(?5)$G}HqTmlx@Hr|& zL3}$CJ@+c{i^Z82?>ttJUviC$3Z&fFZTPz#msH?*XP7%Qpwp1=;IElA?A652WQJ<# z!Zf&jkhfl^0WoY)em*@LXTdt;2>AaM*J|%`1?eWo`@D`=kZyLodvv^l^cKhaf?{Iv zUs2?}jOdl*$NzvVdS6srK|#8z*75H9lq6ml{~%KEzNF%fj4x)!9$?*gC1c`OGR%XE zL`pLL7+t^7DBQ&OD$<8<6mD|-9L9U(v%*a)NaF(y?^`N`6gcGhF_q_e;E?IZReHzA zf6Ew8eo^Ahk0+VWr@krN!h&=?qDktDlrI#~&F5CZZeL?IRr!HcnsVZVVE`8}J*K%8ay5!{@>dm-@& z-iEmzrJ}hc9)r7gymzS-H%Od8U&F={+j%Dpo1fUtz$fUqg^3|9*b}{5@J9AZMkJnP z;FGj(D)9^hFYs7gucRFB2GB*?cV6Om419`rzr;O0v4)`*d#VeXpV-B~OLX8xiF@d~ zRQsNqXl1&~yay%jvcyQTXXwBciIXU~!s9)`4NN>v-)65_0L?DmnWlZ{*+>u(op~ zBj2jBCt0|ZlPBaBxGDY*j2S3gD|EuG$_UY5s%`J zdxaEu6Wqi+DUC}vig}r0CTcb}@h~$r$$egY^PLziyrkFgoS>9dl6T~r81lX3ZSWG! z&i6Pf^GYfb zzhmIl-W8IQRS6Ecy$d{ zT&|foUX`Tmm$bQwN0^gpiSC!&?IuoPzSn8~J~z?9@HLu$z)ify-<*n(8P%tGG=+L?w>8gN3}vW13FvZv2Ni8R?AX)8iKL z@kXPe=g@!+aWq@6Yx{xJNG5i3Y9M!@&s5r;{kW104;r=N7bev06oH00>%W*G}SZ9r+ za-e$&gS%&s=X}xKE=+}U)Pt1w37V&jk#5atH34;-QGOFGEk*WzqAE`u0L4GK)klj@b_!_hOA5we< znr{VsRRXS1C4N=JFUJ?2$1z5Sh-T;tiE-5J6t^tcT4nt^JXpJBTT6Em>Ka-$SJu~;Rn;}{@a+1!sHg52sVSuu{w1Zerj^%~ zrONzm@bZV$&X~HSd^(+$;dJgZ0beam`CDtJ`xn=)pT5x_@!J_wOHZ3shcmoW@|&qb zG}Z3%k6g0zOekb7onEn|VyB$6hgSkJVB=eANrKr@PY?E#P#g3uD=0L6e5%&sg3}G| zRo<08-onO@PYAEB;<4#pqx?=a`&2M}F{ld4z}kQqHr>gv;jBMCPvLh+Y@l4nGx69e z8E+>pSYbYocKaJV%xRw6nN}5!ktDm5h+{{QZmz6rseu=t@-+Q6H&WeE!7(`Q^wG-wx#Y81Vt;WtlwO>sYVA?dtF$$f|Wwl_jS}O zPdC@JwAOFNlq%g=Tia3t7hOk^80_iV)`sdd`&jx2=cHw=b4xQ$LXGMzovv~aP?VPD&5c{CTTF49%jY@ihMKLLQ5J0Eu(@$#y1udn(P}o;G_S8|sNUwJt1sS+ z3P3Q)#IiFME=?~#dEr^hS1dmJq@Poi#usrH(Ed?7OpcN54}s6G`1?MQbIYboeTyo zOYVERu_4X+PfNx12h}xwyzc5ttM$Xz#YdrRDAGi#8yh#)p-!tRTkv09-w2|Ly1sJL zrb=nkusLb<87ZFi29z1~B~Kn(qk~4ETf?@RO59ET!RZ#1ly!@$RQ0)uZG~;ldPJ*R zo14+?r>k*`*Ecr9zpf=+y|q%+Tvk0**Vj}wV3x>7gHJf6FQ{8D?QK)#R>{VuDn2u& zMdRr~3rBF{&{Bg>VWkj!n0S}BNv_goEo*F8kAl_Ipe3qDTj#ow55lIV*5(@ZELYWU zRDDJrT0QPQDA|{P>Esg07s3tbmrrD?(_6sUed3>e%_;{JZOXq{Y@&=?d$+Hnx zTVII>33FxkVJhWxi#)&o2a)vw*}$eNEZu&)n0HP#mc9tUZ4=%;e7Evl%5Zo=I-EI@ z$GAky9qQGkIps%YDlUfaDO?I+D9;$m`?zlFRcE!r5r zUEuBfjetCkotQsVQ7G>=*cpfSk7hXWN34PyLqA@3X=>a@9@#ZUelG%`tu&c%6$po0 z7aGcP_Yt^QFDw_!igZKs5EO#13%SfXvz(=D zhCdV-xz*H>tm|+gimUj_xYBkw{xv-PkGFWnpKtN>=NV$ksQ4Tg8=n5_EZ+E#z8e@k z?*$F#3B=UdZy7_sDY)pj78m`w3d}hJFY?EO8pH6X7zUI)pJOwZVEFUtkMeOIK)#Z^ zHVnTBmzht{e=~U2AN|N*0cz$E{o9folX=7cz zcrlb2d#PE4{a)rv7SmgMIEELvi6Z-EjlpwqH}W_JjeS0{M@n7IF;F^eBE|FYx^^!_ zxyFuC?a9MKH;+6jhV6&^spJu#?S=eW@?%u{AkTiaN4PQP2aES0!?|Z z=zoEx8#LXm=|?pEgr@gs`VCE=)bw99eO1%_n*LhT7|PFdxMM!)L`~1rv`SOf7yV^l zAkgbH{|Qa+(ey!0xtPT8KhTtj8hKfz1^u(;GnqH;pjhcQ+(=TcIf~NnW=^L836jI_cVP&yT7gJV7$bbeu1WwNs<06 z&Ck_zsdhh4(;Dqwr)jHp-=X>4ntoinm-6{UIvn$O=@{7X^kJa80v(_>?BAgFigul*4phrvTj$70DcvCG+a#!O zBf_DVLOIlDVOZ*-^te~qVf~T;)h~(mcUxRURgv**4gd;U~C1FZ&OU9Kyo}H z^`t=ZJ>*IhNTg;%1(NeADO4bNkXa8ENSK$tDUcB4q5_HFmZ1U(hPj;;NFHOtkpc-Z z_C6?(%x2a?1(FZ49=lN>`79F;6-dS~UZ_Cw|5)5mf#kE~LIsj9Gk>81$@dwr9||OI zGWAe_WIOZOjRMKPGrdrOq?+l43M8T+6Dp8g!F)yvBqJEHX9bcUBiMUOfn+CgBnl+| z$`V8hB3M7k}Zlpl+WhN9Uko=T^BL$LI7&uZOIiGB#Kr)AcBLxyZnYIE+4MkfD zB=szWr9dK6BTIo~5T$okASq*UECrIYAVw5OqyvZ)NJKj+QXnZ{w1X&+d>H83R$E;g4eCBK=LHnNP(n^qAUfHBbg6NfuxB3mIBFTOv_Rr`8vf}3M3op zZz+&8(BD!ZkuTE^ra*E7BJ@Rpmd;$fw61!opR8S$!j#H&`3!ca!@xUZ z>mqO-NJDoHlc8U*h@bFE$4?LEleo9PiUF(F>vwEQ(W)LpV7bf;q{)im-8Uv}BWany zp2ER2$ZP@NegDum0++LJw`}ZC;Ar{Fge5~A0>S#SAoWaSka#2EO8QtR^|{aS@3UBj?j7DE71xudKr`Cm=Hym8X_95bO|yPTiA1Gl^FxjXDI#{WWbao z(jv&vR!~|aM#n=bTYzJtk#-OomDW@VwL+8q!;lFg8DM z-?`iG1dJcV0IJErWb5$~;(LhiCA|DYOm>pyF>Vrbho*)wEKMhDUO9*VCR_?G#!gSl z<7Yv}80!K!7(4ySFi7DW(HP@*0vs%7XEzGL*%HgaHBOcnFHt8On|~f3cX7GF7-J<) zWXrn`FImQ?JW?aC3U(6*Rwo85*Ohxd=>cF23w z6cRlBc-^Hbh?teY@aGu@lwoM@#f3K2BnGp)ujIvH5!u?vXH5B#+_IL_+g2 zu*J1-BsNbO%qq*pvYNIGt5c5lGM5%@OuB|Q)OJ4<*gTr2Wz;@u%R(1d_F1d2_|OW= zyMd*%4r!Psv1vfNp<%sdW9#fU7?yns!6!{& z40raSjK5n`!HdBQUJQDVb{D)D{F9m&ycoRT#i0AO`>!<>ycq6+7lX>82&mx2pn?~J z3SJB)cW#N+$TLnkZ!0{E2Xr9S{~ z@KHlef5N#Frft2(N#`8fhkY2(ufb&Cq%r*+eVe1sUSfd5-^}AV@AEhFU4Gs^rqk!` z#cU|7WsOd?`Hbdp0oFGMH*NWT!B{1p8%(Mf*0D(K`^`i1D^87yV=dAopR5juGl zrS|Z7yAKJ1PD;&&=;ZMf9HNtFG0_m6WM2A)PFCWc5}o`DV}|IYeBGV>dHbtODncg- zDf9uI{1metqLa^1RyXM6No-~zI{ABMEJP=-BNw8RcQRy%P6~J)qLV&rxF6^wKmLx; z$^T&r-Jp}D3>l)6moa3BPJWN&3(?7sk&DpD4Gh{dIyo36cyFPT*Pz6LPJWEHAwnlF zVoezQF-XYx&akn>RNe{0<|pPdaD-0&l$8;olbabhLMOK}aD+~BR%)S>Z44Ztld_9O zgibayaD+|@gc_lfOLJR#${h-p+UtIw`Y{ zgP@bIvJe(JDQX)LI{AH;DMBZAPS*3EYdF#@9D z$j7~4Xiy9~W&*Poeq243CHy~t5^|*f4p72;cfA(?7(@lK7qqcK^2V^DvFxUYos&@g zf5a?DlXW6Mtb-oT-PaW*|uqOWfdsC^cT9p*#73{EDgv!{9*7shS!>Kbony7`~@)TvIb~V3oaRFR&bpbwO6R&%%Rm zL1Rp~Y^?8fl%o)tXPH?CrX0&*H*qNAIRI^CmV>dL4nHdy=dICEy|8>nUKQ*n4y?|f zv2K{8MH_~r$;SG4NG{h6nLlJ9l$VY5jlp0b34dcuTG?1%A>=V%l*jRd$=}tmn>esK z)V)vAqK)y}1>P?2NGpF>LJj3*V|}Y3?_pC&grhuOcWDYDCIOIWo?$>uy7$74w$fz0 zPD8lfV0{}A?hzBol1I51p@-&n;6F(?66<4qGpnR5t7)UKIuD_}98HThCVz%E)OP<{ zVSUWMGInlQ9=4>&48)m9sAe&2SH$YrUl=3#FpJ(HRu>IjteqVc<_2N1X5JGP2mDyD zIM69b%mXdg^!=K$U!XgmQ&Pd=KsRW9x2A%{!ChvTTz`v?_!Q#OG zTJwU%ffpP{HCr1&ad}EDlt#I8edjKn05f-H%(!@(30O zDp(w-U~!;=#eoVI2P#+`s93(+1D_B3qC@xt!~7AXbNWQ>088^^HQnaev8O^3lm~&!=UAIP&{p7xb1Xce=e9~ zHkZydn@i_$YqVk zd3o$K<)bZcNURQ!D*4zt#~b<(EKUypLs72d|A^i}9#?{8DB=|{)(CddG3B7TRsod5^bkoorG zR|Gu4sFpicY^!zFW$-}AY;QCK8asSCUCWI0V^6aFta+HbQD${K+{4V|-_keFSg(lI zG_uvu#fOf1vfnvchD%pV9O-pZ17`K9Ua?^`(E_%WnN4O-}<_S8u&G;Xi=D^)~bfeO*NsEftf|KWN3{Ub7-)kp&v~#F51m}f@4+Nm~fn5 z;C0wzxM>Ju;!qCdT}q2K#*fdHvD51-xcGz_W30!)0k7N8yana}xS_Z(6c6WiOJFx~ zV0F0H{M)o>WBm9Up*&ELgZZO7 z=WOH&05VPr7tJG($B#&jp_SL=ni}`9C}dZ2&#MpsBAQINvk;DJ>Bbn*Y}N=Dnu`#Q z^I&64I8sw5^y5au!~hFnH*;v(SteWF9yrW~n=wY7;SIIjd!BpRIbnKsisV_Vk?DMX zFO;z?!}EJthRB$^tX%wzJ4pHQC@5^5=b}Tx^LJS%!ytbgDD9L?ehMhZgUl)U%oox` zYz`Q9!|2%}#5=`S_q$cX8W)$~|RWvgyfP>trBN%4@}q!Kf?!m9OgXw1Rc(JcyKj-3uo6ki-XPq4LTlggtKv}mPEd2DefJ&7i|h@u9ad`Jxd_^g_T8NjX!Px3 z@rw|_aQX4c7T0_2yE_q!+IROb&ohf9=T7{$OlOwjg80ADHPpU)oY@Pt?|#n854GGjb?hG_8l0<(!LwPVcyYDf*aBmPf zx}qEHyI(N9?-nzB$+Gx~Oz%0xRm6WvF4Vp|hV3iTz7vJhp0)3|)6IKJ`|k6|k!auj z2a6tQ-|={xP`@rnthU)$Vhigc(!RTtaU<=!V_1KY_MPbFMcQ{)GH|4Q_ZBNM+8g9n z`bOG!vJXV0eK(!yM%s5*F`-ENPP8*4?YqYrIMTkGOE%KJo5XY@?K?i1w)WizDB9A# zlVgW0?YlqI-_pL@OTM%A-AWe6(!S%7I--3i9YCag_cLZL(!LYjyo2lwG6AWH_T8T- zKhnPA=2DjS-B{kWgJ|FVim6)KcZ(T5(!R?h8)@GSrtnDnPIhLBwC}E_tVsLryNnxY z-~EKXk@lS|+;_V-h`=O~_MIq#TH1Gir7TPP?ieOzY2U4)zomWm5#Ab0`|gAEx3uq0 zp}(blcNY1BY2Vc%Kwq@)s7Y^W-`&Ra`=fowXVqxm-NHh@OWJq-wa9SpRGtly9|Vu1 zv~I4?y>#+s@(3EA)5P4mpTYa6Ef@~D%i!fUxy5j%S&Pf;?@==u5!{L~Tnlk)*b}&G z#?)@Yjv+OC6#ScBcgBvB9Yii-bhl!BWs~DJ`w%;>)^S_jLL%NxNa%-5=t0K6NfKfr z9DDJQ70SLwWUo}RuTiq!FtR^p$-ddhzQxE+%3)Zc>fpn?JO&>nu{xM%v?N=yBMQh(c$GjXWPR2<8aDo<^|F)x0>62Ezg@++MU|7oe&2r*3s zz~G&Vj9l!u^%#?stIb+aY+|ygpzzd!rWu!k|91H(dqtu86W6E`zgAajtdPmIEz*^?nU{o~@w zv^t(cDN^CQjDL_VO)QWe+I(^xLO0u+7{$6?Z)?KCUu44$>Il5|cQ`SdkVHG3*n5h@Of$`Wc3-byg7K9f~ zXzs@*2VBD9GAQFS90p9C^dz|eWgEjzg(JgbTq&lza4GnjI1I>eH(CzHx*#ju&3Iw* zZE1`N$2Dxr*?AV_NFnnq2gZwGIkNR6Tk&%LmJxz*#(H{|ytlB3&T>&6si~JL*i9T* zopP*GucAd8Q_tC05J7cUK#w2F=;iz!7gtS@=X3XUNQN*8g>(h zGQJ6UNm{fqe!IZi@+L!GA>5chLDQl2R^=J{|;fMt1@RZ^DKv{6`{ zCbXCNv}j|}HN2s=d(T*vodVOdDhdKSV^eIYks&t4dl?y=1P$i-(810P<*_A2rZYCh zI~f^YYQpAO%aQS+897|DYeA_eY2O>xBcFHr@dnbr9Ml;4heOzC(-YI<^PG;0Yoh_L zanCq9Yd%DbLALJGlx620D6A)?z1xsB(-cg)sco-|vnnM0LZpw2=`YGkI}c%)c9TwfGqLM~q}^s=;Y{~1J`Fwc zKJWD+nz+Zs-yc6Ze{s<;cepzr&3a4@RC(#RxQMMB8bbz&COfXQ8&Equ#lk|di)|Ep zLMIeE33+%znIJ%c(;A2qiC*hEnR}8O!AtMxe;5=54Uvdow#=)_W5xWh4x8sru9PbQshX!;S@*Vy` zS}af~c94N$2iYh#F~cB2vEK)Fb?~`@bdwW&o}$o_3NU^R?orhm6r{H}!50)0ixb5T zLKK^jS&)HZ2lstSWE93PXFY~{$o!66h+@BtFhPi7FC-VD*hD;o3=})aK(T{wsT5KL zX&inVJf`wI4;(W6xXS18@$WIc5XI&p13?Cg9ei6!SrosY>1CkUK?aH)gedk*=JWec zK(be|$_f6+g7%DJe-ZNETPXH=v8OX| zgktl_3|hS%Ab!cSUg86+77N8*zzWDhv9XKSaTq}PCG9zhy$s(O#U8=pSSa>Xh{+G~ z+=O(knJ9J;q1bXfQcoy$Fu_gmd#xZsvFEWA(H=5Xnh1J{Vh0<%1i!cqA{6^YM$bgC zg9ycLVBky?JBU#1AJI1x#SS7AyOfeMQS2bvLxz*jAVRU{QB)R+9atzfH`EETQ0yQJ z#SXGi>>vxp4zf_}V4zU!;36-GN zopJX;f=iVW_roHB(RYi8u>wzTCnD`GA1lzeF#b(%IOCH7eP1h5DvAkxe}wRK66suL z;OTJ*4A$KLrtGhXY|7<;&Ani36QS?V2*!SnO6W6^(3vVBVf>p8n1t@P68fr1=xZjS z3BZmC~bdiBz!{@_x7OX>8~bbnAOupS9{_sc6 zm{K}>TG`UFE_^Xz02t2oHX92kZwy;#QZuw)hRfjIS8Ck*RN&qw3|rk+2?P9m#0~z{ zfeOViQ{aw*z)VD2<+ljMu0Sr1*K3eE{-d zWW2Q=H6qBkXQ3ztG%6Nh3L?q4hiV_lFHZa?0Kd~+IO!kqvIyg)?RJZ&hvmGH_)PuspmD|ZF!3zk99zK zERK=K1wdO~Ie^jWa5KjEW#i1S;Zci#NmvZMW9gT5GLswRNrQ zqs3>fRjXZh*S4-ph_qJi)?MAITmSX{`+ev8&Y6433j}H{12^A%^Z4d*=FFLy-^@4b zVR!R)KI9Q2VE#~~NM1fpei7ttvO*#p4$ur)l>%J>+ z^0X$*HkUvtS%%p7IL8-`M?<&Je?t@?^nb_UBP%RB16Rj(19?91AC<>X z%H#RIGyiMwV|*JPSHz!+k#fg~4QnuGS_f5#}?2 zN?b>ZhI1R;;r}uz{8@K&XCKlhE{_5VE)P1I;X$jE3N8Ov3#^X$n=yc>5?FsZ{hJ!M$Gj7Nr*`6+KAs7d=Rz zx2gL*N*`7FOQl4;!(Srp38_J6wB=DjK z2~_kTfeJ1UDteGWMGq3F=s^M%JxHLU2MM$mFFVtf;}oEx2MJX4Ac2Y=Bv8?V1S)!v zKt&G{sOUig-8UJ^5j{xYMGq3F=s^M%JxHK$YdFz^1YYzYfr=g^P|E)ObtkU&Kb5~%1w0u?<-peZz9rXzZgKt&G{ zsOUigou&Sw2MN6BK>`&$NT8wz2~_kTfr=g^P|?!YDzAuo6u10Wwe5QmSsr)GAXDL5ddFhAn$0$TeJg7TqgXRr8I^HLaKVchi z{B=wWRl>fEJ$M;<<}5IDFH^hOCpM2W-CpM6&cwLKy1stY`pyfh7H=LnY5iur)tduv zU1!HY;_J1Wv)+TR5c}^H2ak_t=jv#3apOYO$5e3qped+N(bkks0}jT^L|siphT(^= zyq@|NWOVUJboVHc98DDl_}_gqQC+E5aw<#znnfWQ}Of9Os|3FaYihxzAkyv zb;;u8aM=3_`YqUVrP)g;maTW-FbWyOlnWPLmpu78^qk%SEQR+M*mlT@>Z(dnz_Wc* zR$ItiRkwIK_1SEXlrRI86&0o|in=l6g_RZ2XuT7SXz3ChAzBqKiQ>sgv%-@YKW6UO z%E~$G6QL6F{PjxqBWCy*aCYYse?ubN4NAxMo`FTHD*U zl>vL;pH6e)OG+6vW8uEwWWn^PRA4o9+{H}$Lj-emTA^5Qu>MPtBgt0iV%4GGIw5TyZu0fccUem$wtutfR!{LD8KxAIT zcZTx}d7hvw2lK@_g2jQ=;~9a290#+`hMz0%c}yA^pYkwFisW$(!s5UxW8YDd0&8?akBW)3_->hnGo=_@^_N8Zj(Po>0G4?l=7XSf1T3hO8Jh^{T!vAQMy6t z=aha)Df<rBt90kr zt*NeQ8|Dv_sxo3~2V8i}++(3a6_P^ZV~!AWp4ZOA(44D;g!6~ww6R-c{=m6IR_71? zqg`JgxUTcUWbNjGh3htR-cb5?GiP9nqhV!XOpV9FVGb7CW3XtjwP?v|d5KYMC>LJ)SfC3w_#N z#oxjO*CjD+fQ?#dKl*b9zNGz|GrZy1q@gmLG@x6^+2i1TL--hGmLxZz4!fN*AX)f? z-}mrK5q}S?<8(P<9pwl%oKAW9*_JpzHMteu^o-^dk8Qk`2t(e^r+rEu@mas89 zhJG~pbB3P6jX_w8!*~qWpE<)MMC#9+;aQ9UIVRwk9K)Mv%$q-FxC}3A#>t3TXCsa~ zB6|YEvHr{%ZbJq2XU^~}VZZO%}PjQ3~GupAFziX{qa>D~%Eir8Xa9SGN-Im3?;u0L~z2UxjCjD2}; zbA|`dUfllP@@M%n>bifF=M3xvqB%p@$&Tclf!{e98>9aDz1%%#i02AYFZjdvI-E1C zT+**z#Q2k;!9w~_vfx(YKLo#uyka)8-ea88f71jCwaiKlYM$d#<{r5R4jZA;Bj_$FEg4|K?xCM%oeT;9Uo(?g5WoybA$KMSU1+WEVDp$2 z|IC85B*)0WK3v-`M1z4#Io(h3Jo+cRPx9}M*g381{Fu##EmH={(IWcKuvK_j>hy;3 z7cNVT#HQc;@i6DeW|)5{zq;DLG;s;EUpZsqhv9Ptev*O&=l4q=gYf+0K8kJMQW?sQ z7tEstW{2TFN&kB9fTj>fA-Hb9EZ5= zkK-;HuAjSOz6{0$S-5dr!1y*sJ6L)2kz!s^YDD@PFo)qs*?om$8WsmukMh`Um@&WU za4xuv;8hy&}A%Tz@lTejGEq@=A5ANqH=v z$*YIm;=t+wUh_E4H^Xo=XW{3{8=_-P%0pHod2O(}^6rGZ?#99#GrRIeIr(EBX8F4; zPu^3Imw}rZOKUwGTzOe1e-a9HcC(wNY5Tm*K!QV>6bn z@kU+uzQ@9D3QUiw$7bvGa(i3DFw}{I$KV)W*0*nqT-@AHK3h_3c7#qG*1H+jTSl2X zK{;0bnDQj_ADhQl^+~h6DWzCYst6Cb))-R~sE7ag&>)y(G?&Ayh%TxZ) zy~DOQ6wD&>7y>R=_sdD)emyB(EY@b9`|~i-<5CFqY>ZZ#RVwTJaA)5_|6`QSRk}c_ ztnLtGjlAAJ0VKFt2Ph9xZ9t`J0HB?2X`V&2klqA60C#0h3os?lN_7lIo$Vt)uyh2 zlV)sQFtwvI$RszFWWLae=QSX>^U+SPqoXUG?dTkkVf@d&Qdo+yycdrD@$6rn02#nK ze_c2Y?g*FIE4i7vMrsjuF!X@Dabh^eADo%H*~@O~nlPSe4BLc9w0{(5ToUsKE&eZVAX>*#qPw0e8#i5ai=U z`1f}{;9gfzutWAf`Gfki`zd$A;p~25qs0Cvf6&RO?!5g^rF0K>M(q{vj6$bj`=th@ zeMTER&`V>>6nf}*SJhC+-6yLPmRHmb)^W}k~p{%O(tiA4`#kp(h)!nUYWu$4-N3=1T}>3cS2J^%D8 znBRqaHjXp8a(C;(icj|S?)8JQ1W#_lS?_T`&T0z;(OtJJ2u+p86XW$h4 z@z0ic3fSyTcg6{2wz~Q8N~jqHeS!)+mjb=gqu}@=EHC31N?hwoEd3<>>Gu}>r4;AU zpIWg??|>iO2P0QuE|*9yxv7Hg?=g<#a;YnGZ@M#1C~sLPFNKBIS&Sfw)JTN+*CkOh z1WxjSl{I6hm_P47AsvBf-Z*L>dg%3o9M9~aDesQBeh~ZP!p&{?X3K}AXNU=P5zVOo z9T3w6;}31zFy@8I6PB$C23=ekv|K!IPBo9z`HkFQ7W?H-_8WPkUA*bDV|UJN-;6$Nh`4!CTTnOq@3sd^lZVXP!osb8%Nv(1 z|0_UYfA!UZov@(}tvY}0pbBno!?xEcf9>D_OcdF%mEp(thT+KPuN|CEPsbR07~B}% zW=%bX<2S(KPz=NIo;5ROoel@H(~lQPu$M4ne)($$kD?sxIam%3Gpro>YX>}s@xBQI zHF+n%9uAj7&qqt+dSUrYUcD2CA=e=>c1UKdp7YlZo`O95m;4O?HF+`+u*jhILLQ%y z8OvY(+QExX{xBwwIcO~|V?2Gr!;qV}x% zEx;h2XOYa%%HQ{un)@TDT(|x#3OjDJsTK>z`>gx3#;_j>^@K$NHF=~?GxE;IgdqdR zec+CwOL<9IC)So>^}dSA;JanU9_h3~WQOaZ_jhbA5NjnnIiwUNv^M|&=_ki@5N?%g?n$q`_7NPu{ z@7un;;p7poo)jQI$;V)D7uW7h%D1V>&|5)iG>MrN?5dYuQ{Wt0^=l0XR+G+^$HdluDW2VpblJOJAjqfj3d4AY= zN9!d#A4QY$uEAQtwSp~*gZz2t!v3xS*lf8VoE{enMktyq-SbODxl{I3Jheu08}UQw zVZs?TtI3RiJZv_jz72NFsILaWjGDbDX4FdF28S8-W1(XKTSh^^6FRu4KDiXG1)PWn z|BgSHT#pCAg=?6Ut4EA~;?IFn?VXoljaRW3aLFov1! zKm(hbmStq5pEE!BfP-|t4~u)5Uqn-j?Y{SXb<^s$h9%p2MrPLofgc4BkYhi*W76-j zrA@)lZFLw1i7CflhO@Csb?6-Zr60{9_%Tjj;l?10#evnMycM*lF+YyG%}%fT@#C9g z#{3S41IJQkUO^`A1%pMyUiZ0O2mz}t5~#_W4ZAC^0}RVWc^GO&<5iBkEe@>STVOJ@ zs0q7x&*S>PEAKWi{Kilo-5HMZIqr7l@%w3G9e724tSeXEyZd3{)hUe$mw#-DAB|DkbnLwW8KB+r%*o9#I(Gf<E;Ev!2Y3-NJj;y=n6|Q{TM5apvZWp?f~LvBRexd+M7v-Sc)Ix_cM8>9dE@aqKa> zu?+aqAl$cJW_O?`fp2mDn%(D4*}Tu)n|9J>%B_YTeWuCuc)vSvxh>ErvBmCq$H~G_ zr#`SP>eTNkz5M&#S;Ah&vMz|V`_A?K%Yc#{f|u)np48J*tG%O{o?7i4CHKSFuAMvA ziO6N#(SEsNZ_svdRPj{4#Q}ogMSuy1|6I~GRXmDdNui`GGiUct^pdb)x=jFmhBf=qdRFe3dl*!T+ee+PELWco)CXk0425fO|l zPKzcERczn?E7&<-<`wK*mx`}oAHXb3C=bh#eu=ayANXKaLL4oj8{4u5&wW>`o4mF3?s(M>zjSBeJHR03xvxR z>{7Fl^7QzGq`Y2MOmdXhCvue6 zCvue6Cw>xE=+v~V6h+GGZ)HAnl-DOB<@Itg_Rqq2bJJI|d{2g4b^37TEK*+oIdacE z#FC7F^7@llh`yEAzlUHSF6H$#5Z`5i74RBpWNQ|1Q~BdA%%Z#mei6RXA6$U&VxC<@LOwBM~dF zKY))YR$hN8e=P4#uUO$n(y(q7r%izux>~#zu zE3ZF@Y^=O~1w+Tm>wiMuSb4oHf5yt|U!ZTSynZ6%#>(q2W%+WI*C%4-^*6IFW99Xq zr6@;veGO$f%Il}o-%(!wEj}7Yc|9j)?iK7`qQ9fOejfcD<@Jlm_olpl6#{I_73@@> z?p(pXh3Rjr^7;gn*YjPqE7>$xkUrAYhb*2u79J0Lm`XK11A4mQ1+=Oz#J4|&iRE$4gQe}!9liAhm$4Nrf z2TDT6xd|N?CiIFWw7^Mdp(S*(B~-qMPc709 zw)X05*!i%%RqaSV6M&~Z)09QsrR=gqEyF9!HNp4nFWWro7d3|kP+Z9^UX)rk$k zdLOs7dl~hlU%UA89(TLMogu=#yC?@t5#|U)KWm~ofcAO59uY6Wm}nRQEnW?qu9?1d z(YH082YpUn%}4&S_U&1i*LSKis&reM=L9WBo#}`C()XwUXhk)PS2nldQhD4&PaUmW zLl`x!Ra0<_L0vnr-loRp`ZgSA!Y%aOY#f5}_hn6WD{FYMNKIYa;`YW>4I#5V*T8Q( z22<1R)$hG2xsNKstAUF1hLw$Nc#v~XoH3_n{xLI7p1>4lZjh`s!OySKORR!6_2VTY-s?{1Ho`{}R` z3Nb8vcVYv;glk@0*X4JR;V|~vYL6Ll0oNFN7#tZM-)J#Ch2#3Q#bH2(<9-S=W}OZP zv(xW;_#X1>X~z7x{_U1y2+A=FnP)k;7H#F254**I)%zzb**)MmnDqqsx$kL&WTyeA-!vt={pN36@0ce#^4 zu0oo;%V4)Sl<_X)WoS`je(S-z@~(wEuH7?#h!V-;-WtZ?^P|}hAV-%K5O)&ya)D!zGAqUv2=|$>bm!hVYw+VJ*T1|aBIwpEj2bgpSEYr!sbifxoj62 zH#d~$ych_uBleQvv58rZzr?UIAuE4bk7MzpUps#ECmv;n{_%(wgR2nt${F^hnC_w<9(1wtq8}c-teAs-N8Lp~Josmn-+ga;Sbw|kZ4c`~^uwclh<; z_7`9!w!J5PyT`V9#k8T>sq_X!Fs?ZL35V-Hw*3RdBDT#vdNH=mTb*NU`&Ify*fuX_ zkFo7HS^1O7rNpJ_o5@WMxxwjwr<4fWK9O96ZQn&Hx!Cr*5F*(2$0#l5V)jIQF?)s) zV{Cf@OWg;y{T^~9*tXPcgl%8R44oQgHksz8!Wi3TUbYR}J{{7CZNJQz5w2s8)CAiuVzy#z`x=%a#IH+x{BG zITy2^L4OC^KArv!w!NHuZ`k%)1lSgAn;MK9Z2Q|xe|xZPzN?09f18EgS=e?33aHpN zXS5Nv%}qkUw!a8B#kOa`#l^O<=D1DR_FqsJ9~|4Bfy(Fu+m?hr2)3Q!Q;V=|4#5(h zw6xvWHm-Qa7 zan7Yp*Z~lNmwy)scXT%FZwZ;+1z_|CL;iK;W-&qGw)g_~zxj*X{}yg*-+4&3^%K|z zvAVUjp{c!Qab0`EvgX#cUQKvHQg+0k6KPzDy%N|>)Vvxy1(vN`9dZQHS5B&`3wJwU z8$`|0)lFz4P|~`11$&z&*%P6MEd{u4Y+J(31_)5MGTtb$`-b%$2WfY!H>E$A_FMaX zG%)SrZNRj7M8J$$Pk;lKyd$#^Kr1GIvhGcaais9|pcV&KuLS@w*V4=|9L-tyx$+i4 zUIuQ1@govx^71il>VK((zZpv_AJabH$zN}n_M4EGF=3!4Z$0d8{x(1!*X)@;M2Yg3 zk7*AAroG7uiExz1-_1%v#Jt&0O#3h-h@>N9`OC+&k4L!vVA}2d#I)~5yFV&QHLe%O zNvxJ1GP?rP=Ch2AFLx;HN=$oiO`KC*V%F}nVf#1~Kl-(+{{&Do^p61_2hxtlyFj!f zd;A@0T-CC=wV{Uk!*=7@XmG^eu2d?EC*W^Wem9;S){oqGj(U;%&Ov{LK8f`v_nm{v zednOKqCni`zH{&+m6!X@!B1CS?mGuxtGwKI4qom%2bKHILFK-4P`U3MbT^(I*6-g6 zp1p?e5hmpP&bfMxtCa5(&(=wiY3}J0&z{`>v)WHEQ~1mYJXEl zC(lf;#Mx*&FU>R3-o{N`7oP|n$D43=d)m{`dwkWF=-hPn#w|Fb-GwvSTk_9Lm*C!X zo&{foGuht8>$dPbxCdRq#UAtpBTb$Mw=>`u|FNUXJqKPo4QXvcTAPcOw=->)R-0d??FH{~yzt5OT@1 zXsC)nYk^Io1KEFM zW6(OoQuhH`zk^%}Xe~7xf!3EZdl6`T5d}pDvYD4{1FcVnG=kR8F=hl>f0|PBL2Ehi z9D~->4YLiP^?no?f!2#y9X){7pJ7%a(E2TM5omn~3m<{j)qHLdX#F((BGCG2rneoS z_0O4J1X^FueD(lZ|2NZ%K#G zDU;*cF=#!8)f|J?)b#Fv)|WC;3|gltDh92eXW$sLK8kD%TFd$R7_{b_>4Me*ygQ)v zMJ$8^TEEAnodemOrXIRFQD~sq$Z$sz-+~! z^)W0(3|b#d-(Em#e#u7X#G{jjX~?j=^G!&mR-X! zXnhXL*CS~C71m`8TAxc%4ro1y`EWq%@$`2<>ssc-0j)nraSmvGBK;lE`dIoqp!Lb* zdjqYPBEYtQ)~_(b&58L~7E6rq%D%(&w+FQ5yK0~{x1Q}Z(E3-%aN%J*W{p$WKS61M zRL_B%BGue7QP{}C%?Dh7;gEk4^5W0){c^a|)Z%Byu&4X(Iqd1aoR2AfCQH(dS|4VK{zW?Z3mS`#jRy#PlYk0{x~iJ*LGmm>s}w2vgA0 z-3^+;>z=02?55BXrEqqXLS|1sod}!`4_<$YZo9$h?!oB@^g<==&|}w6CFI<7MNopn z)w6l7`moBemD#H7@?hY_LCNf(b#~}iF%kYeoqb-#m@t4Iyw1m`GiWGgYGIjK!gJL) zPi;r5;g-RVrWU^z{BSUPX35~IlN$=w`xhrJ3g(uq3GW_)pXdD%?U9G~Yuch^I=&Lh zNs#UkYMV7MK;P%CKl%r*w*!v0D(2Z_!OxnPE^TW7Xtt`pt+l=R+{JA`;(*i9p%sUh zjmtso!sD(>o7(UIaXeYTJ6vDo)l6?(CI@$y)va1pCw{O2sasXI5}+LPTDPx?&-{i* ze9v9Hynee5Ov^Fh=m4<@`zxQ7{y->r)E{j7iR<0xtkHhk+R=Ph)U2!{0=%@TeQiqv znrGb--oAG7Txb#owA=uV73~d#yt@OXc1w$`o6acEgx-vp;NH&ES9xy+-xbJp&s0^=+(`RA;O&2cG<%c1xRM_A;+%46Ip zh6_=yF_wF-Fv8+gOiWMVvhcMy49IZTI1Xl=o)?bmV*FB?v2aJi!EyGULOHT1B+F5V zAIr;p<)5>@7$5fjoU?upicT4y@<>fyJ?xeTSiNZga8IK}jn(s6;NAQs0q$ksMtQwq z(lHk7FBpn$mZ$2hnjEwaMlb(XcJPks4T#e;nR!R9Rtc}9ztwDR4N{bpx*Lb6@`>w#G z)0VK%@E*{oA~!cTEDu{!Y<9#sYt}nWEq=tLIRrJsdK`scEq*Qd(LW1nhW<8wt%xD% zXn320NQbUD2dt+dI?q?%p_Jv`eZG1(9-YEVOuTttrMvOyus)u~hmQ5~A4*?W`e&sB zpf{cV`zalzbh6T8m7c70iPAQu>y=)o^czZlsPqw~9~6)NAAHD|&v%vD`Rc*wwCSE9 z#W-N3(hcfk?WxoPZ{2~#_|O0w%WFCDw7tNf`=U3Y$BV<+ys?J9qzqjT@f^v#2C z*X{#w!~M2hcfYo=^Pa;tcHaB*jh#O@U}NWfa8GW!@nz8iuGq2%oSL}uUvTd%6Womj zqyP8-SaAA~VqS7P4b8`pVBBUaNN}E}?yYd&)7dQf3>5cWB6e);Lo@Z|4kS+8p@{o! z1}Q4%-@D)k+_x13xG#^&8t&T+Q4a3=34~VM_aylAg!_iEl9Ld`!F_LKi-tU-<+@ zxbLm>%f)?vjw}f7yN-U*VP!eX7avys1wCWj_Y$Vq2kyH!@+i2k)NF+N&SBOg-1lV4 zjc{M)W!rGyIy_V2zH!8u_^I3rs4;eK#V^HsHR3{6x5~Tujyj?t3(wS&aL# zND=NU7mh`^?@bID;lBGb(-H2QU=42v?klR+Bi#4DnL-b^?-5Kd!hO$Y$O!lS8S5%K ztSs9LpL>WQcvyKkEq&v@d!Pg#F5LGYP-4M-XY--NxbHgFgdw}hGEr)6Y^v-~J_%!s z%Z_E>825dNl@TlAZe!pW_q~jPW89bXT?hBQiGgF>H^aa&?u&h{F78Vd)xmw|@Cn7Z z@9!Bn#(m|;XpH-Qk%42}mv5$v``$-y2lu^}!W`W95muLj`;MXi*0?V<(7Cwp_Yh2Q zU+Dm1+;_dr&DjQc*vGR3&> zDPJSE0aU&p4btjWZl-vQMG4k=Bg^xcN?w&UnKcKCx7>$O|?tzccb1!ae z_fPT;IDz4bnEE4phFv1z!$9Q{sM;Ggjzx-4)+1Om-ah=h!%$Wnq3uaKbzAG=$`H#O zJFa3}RZDAwvN`8&cd&lzXhO2>hj=k=M9ahIZIGXxS9xgFp_P@{v4>Sw9yY#m{>6!8 z_d2Gz255$l0Xn9y=JP97N4jy%cr<<@-8&NxjPD| zMsPg6Zm_1NZAs&j#->KV7zSLds&|mMnl@+xjt|Dw06+s@zcd8p^3T4VQzz%?YT5|) zIR_HMla@6rycz=lUGOYEZ6^TAxvh;v1Zx&IuclV!@QB`4fT8F#ZfaQ5j{N1IF%7F4 zT9-96Eylq=)rhQT_qNsw94bSusX6xexyJ|uNl4N#$Q=AePWADOpePNFA;wteF!4zo z7OZ1MLXq&fOX}7}Pep1xhEMezbFPAAv~@X4h&oyoo=Sz3@Q7g?6L`{^#`dh8VhmL} zNAT0qmgY7bRoo7>%h4%EI~U0!v^9hnYR&TI)vYm*8mb`{h01!Ivxcpr-nry52Pe&8 zwi~qMfqPpmV7A)AX3^YK!y2gx4~BOG(i-r*xVd>nBU)8`ov6wdjoD#I^3da4=(&A~ zz;|qC9-b*MKGw|IhB{z+dKUCt%qe+56ds}PDn9x=Kzr*$sf}$li`Ue3r>z_Myc_D8 zR=0Tej-u@oBxfQDd50>WSJkbNEX7>`Qgxd!@O8kHh;D5XU=;NbIHIe@5#wI+6#wH-1=PqUWQ{s~!g=(E6vN?Tw3V*t8{p;|hNa2JFnbD@MOcf&cnrtA(Pqp#9S&xvA9o${3u(su zj)a5b?7f9@OhM*Z4z5XBIr1^g7Jx)Yn-GLE>j`=C#^9UFdZ0W~lUEPB#evnk8z1&5 zw5VY?nzQh8^H&9V8Msj%*1;lqZLquYUWdG~a5H0m`55LxCx0AUSpF`9-QrLNPHITH zw9}a1df46kErL922fbmKABVgrtdJ^?GH-Td!|M5a=hlm*dprCvZf#*0nvWw~e=y8b z5w1TN<_26aoke1OVVHNLy|7K1vHTft)OG)BVVJd+nds?-4m%UWKN1x?_Y3VwjIjBLZ(a(8MK!=o*74VRSt1}WcT z(leDVSGqyz=akA^9pP?M{zpn5R{9I2zgD_M=^vD)&@h;e%$`6;D$hwA-RqUMD7`@G z?(?!?{m4oR>S;GV8P>?Qt2dh zKV0e2>TY_WNAP_@e11=fB(Z-YKSp`>?c}9@!5^dSv*?ZiG?cv5HyKdb7fx9v#r*@kBB;-%pIDIqrsNHwrksVzFftv>2qE6Jy0P;l}uiQi|m2Mf&!&=&+ zE~wJ@^@Ss!=^FU4hr0@q7I4(g;@KB>CXt5#dEhc?n^dzeyXf z+`LsDYmvulx8N^f%vm_@Jh5`d?k`T|V{Y$rps~*G}yf{~d{= zs-bG@q^sJxz{N`oaa@-76XS5}6tvYM_b6@3JxaTWijY&V1A4mdIGualaj~AHRZMly zG!E(#Z^fFm;;HI0kUl5lO(;$KBzwi)Ly@h$Kjs}zgk7{#O4-8=i9C!!!&k_E8Gr1o z?P}Noy?zoTRS^7uT#*s3y85Yrr&!=v4K7g-tOy-tp}Q#HS+7)4z%Ows8BB&>G8xo> zfNO-KUzn7yr+|gYo8gl9#3cMJc!Dv4GQV(T+qqnw`y#zYc!jIT4puhl6}>9q_VH79 zz!H@DP2}nTiRgMcC^+ zddze@uARCU7lQp`#Le3NUL45y$BLW17v6gikdXZBkOkvam5ZBKaXDi9){wAVv2yj2 zhRRiSiv@QL~oDIJ|<~PTWt-L=mto!v|Oq(KeFRBcOR!%D!aBZ>ei- zZ`+R5!ez^AR`;~nr`gS2@Z?%=!;*|Fw?%82ZIsg3(9lp5E_2niEw5|A8W(obM+;vy ztFg8=VLTST!iD9Wr9_dGHAcH7fdtf5s#U;V{i`?<{%tGtGDk>_xcclqjN4S8## z@p^ZAW7FEOhI9!pTxrB=Sy&oKTLp1{hYMJk?JeUb{hfI48T&=|wa^~T%U{b{R1&T^ zaPH5yf-e=rVyq)P)*ZyW;k+_*FgAZJtEX@rTUs2(XShGoqQ?BDgEu?9c*`z(0W;=z zBpkvaW9V}|%25qZmV+N%E604;Ee=DzOAp7vtS7+PmG?3RaI6Q)BQ<&Tuv;8hz4r=o z*F+eO<}CbNd9Onr$I+BG06&wLzm_!yna5Fp$XHskrvjjk3%n%#Z&+FpmmbLt-ijGF^<9`cch9Va7J6QHA&W|dA>%D#yH$0(hvbb-<{l-4O-u9WXI z1#@5hzb89l#BE9v{IIr{Ao(hBt^J(Qmh-SRsLG_ zzh3E=)%^}qgyZH=#(PZZOG^JoX(7^}|Ie5o*irR4>kCrP_+ymMo8j8T^stm><_%-nR-QfzB?b-A)~0y`EUf1BeT3|c5$AC$OJGp-y!dEHa&$UPUsr=yQg>@7(_e=`<5KCf z88_O=J<8!^CwFqpF>p$Z7>w(VHZi;0to#ftQX4PNgtooFZb zzavgE+R04-FFCOsZix}a>2IPsl9NKNG(DGGw3GW8N|_S+4M`tM?&BdhG<_?jPLn^5`*GqmoWK!w&tn zslm3C|VZrk0vfr`j@lNh`){^bjPL?fo z*;LuNtPArkF1vtn za<;1bnpt)!6N)u>J;cEAPVS#DaJ-XSR%YX!+?5O*@8ss2nOvP%2NH~UB2jh(MLRpW zFJU2^o!spAlg>`=PV!stP7$S;$wHQvenGV>bmc8SmsigucD(tY37 z5XLRun~+=oeE%tO)dzBy95_+nb<%;@$4ibm@#lNP?tv8lawOn?ArwOq8$`^3*g^|G z(ZuD?KTtG(gqWSB(P*Qa{HGf?%dbu)4?u(ILu3;a;u+86>Dcl>TK75j&7jnVp->~%l*-h`hD82im^oCOpSTfq7{vf!7*T}y+ zY=?Sdb5D3fV-t2|u3TAJ-`G@HQNdm5mCF_{uB>ZaHMXg_sbMUq@RbuPsw%1~6?^DO zQvbHPf3VoHQQqdAf_PvTCU(sN<-p_Oj?89Q(Uwd*7+PC|iNKB!OB$Ajp|N3e1#ED| z5Err0Rxb+tilDUOvN>~t0Tscy6=SnwgFPys>#Hg_XX&`Zve~L}!QS_sIJ#oun94bo z-S}bx;rp_$$Gxk{UF%_E%`h<-+Rw+&^nP7Z5bFJ!1-)MxxYMxdt-(+9hYh|uxGWil zVwgkVE*bYGqQ&y<3TNaVUy+*7jsY3X&?u;sSKQd5&Ft0;)(}D+JX-7S-NMy5b-1ky zd)Q^8bGUmtJ_ukNv+Y2@RvY|tHG=)kDFKTbQM{d6&Q9d-^AFD+*qI00T)ypl__YD= zDt%tt1FS8slzyf%%xcAA_k>8i7d<>Y5+BNl_P*DI+rF{K-8r8U??TVtP9ANeud#jd z;Rf?)zrLO?snJ6#;of)VL(Xp0=xGW&IDm5iP0d~ivWFY%ZF~C%)eV+& z24N>(Sk=&sGc0vnbY@xPG(fyn|L+i?m>`$0zK{n+dFub!x`^ zh+nw!);Rg&n8oDfW0K8~moZ_WCT~6LuDo@SN34MPLy;nR#6uW|d8fGs^8UjL37&rZ z-K-Qu%t{FA4r{y}zGy2g8qB)|;f5IlN?A0-39U_$e;DEJqeYE{BMrw@p~vf3`CSJ` zLJ~wFm{pdGWp(APM0+_NZe~oL@kU+uU4b=PQ)in?pf&)pImVZAaU2?&(0_xm2+wlA zNqv7(bpc*k@v-Ae1`TKwrrz zCQjp>vs4f*jG1$WfYBfb@35X=v(jgjav_=Witz%Ae5I3=&Qv;IX{}P;Tfy+>E4^Fk z!%AOL%Ig;xPWJqR%AS8v(Mbk+WHR)BR;k>P1@~8!e_N^Ckp*|TBMVgS$O4r+vOuQ; z7h^hdM;55ukp(JuWP!>ZS)g)97O32j1uA!Bfyy0Opg&VNFDQLQ={rhOXuwQoh|)@> zM=719^i-uwm0qg!8l|%5A97^RKd9{a2Nj)UprVruRCJPoicT_6(Mbj>I>|sqCmE>d zBm)(lWT2vx3{-TIfr?HtP|-;SDmuwPMJE}k=p+Lbon)Y0MPWIUsgPDFouTx6rI#z+ zqEvK}A-?D&0~MWQpo8#{V7(ngiZ(Yw>7nX=hSH_#{wbvosQVMjKc(~#bmo+I4&M*R zLsughKl@DbBb6Ve{4C{VPXqk%7`oSChO@b?b}%5*A1r^uBm{%09dO|>bC30XWJryV zUNYv;5Ay?pDq&y79+X&q78tsp?dY63?WQd}3qYJc0bHDW>#?sNX8<+`j_!Lcgf0hg zzkOEs+k4sTw|L$Hgt?^b9o^5~>wa~2lX^1Vyk6k$JTHtksHcIb>IQXeBFC$XvJIkT zk8*4luY~u2XrnurbR3o&-HQi$X}IpmAPQZBEz#ZZ5Xe9S8HgfFHCO=We5E<$n9HW88L9(VSiACe^jxc z>~NTh+-KMp%j13a3Wh%j!FZA|NogF_Vh>NV`9EfN$F&#itsj7{Q`MTu>U%-*w+ITg zX4INuYR%q-3^S=Yxyj{H{=w-&N|_RJL()r_ z_XrFhODVZv_-G^}YRxi?p96*`Vley+3XQ?=I_9kpF#I>jm8dn7nvKBle`1DC4YQX_ ze~W@5Fr0bWHnnE_xKpiJ31dcJI7anbt2HZS5n?d>3y8T5YRzQy6oKJirK}tMlRMvxO_lwCHDhdX8I>kFYR$5&l^6{F3`NCY_!xI%>_H zq;Cv{Uqs&+3~yq(F&NH$K#p3oKQnL)h7TqigW+o!HwME6@QcB4zL~CCvsD!BfZ@e# zH4Yd~aM4w3CNqky!SHn~jsu3@j=B)FX3_z~VE8m9AA{lZ7_ApDoZ})W!0>qtUbOF<7%e^veOYDPC*`O$n@2VV!&fqN42Dl(jl^L1!>o`P3~!-t z42D-QZVZNhgW1jn!yUC|PqOY~Fq{)kSFKqsWjSDY3$y8f;a_G>95DQTigUp5%jxfc z;pfob0mCmL-y0atm7o5C;ZQwR#(i;)TC?9U{cQ!qp@DBdzN@Cz>|j>PP6ES|Z=ry= z!RjsKJGE*8nBdb$E_K;c79bm@p+WM zOYKrWr|X_aVhq&@x2c66Pu~gWq62)+3!sd30ZLdj^iQ0Pj7X$8{4P3htoS9`Uj;Rd9SLpnv7;ldTCPaszw$}CCW!H~m?#ji9ygz1fbO;Q+~9!{Bu zvH*z@L(*q5-eIBN(DbWJeN5<=Nq>bQ$A*3*)7A8=2>nK-SJSUDIEVi<#wXH3Mh20MlDy>6(KWfNn_PJCn7l;bI&}swL^{85@@&z zl~{!;$QV}?tF=gMtf4}YU5+|4D8LJKmZ`F{_~>LiZZfNDZI}Q(a61xXrwY4ejZ1Lc zk}A4-xnc_is^=yUp!?BfTXG%+9+I>10w>1jBV}(us3i`tY_q6fJExs%$6KG`tP#zA zqS_APRyP8g4E6NEimY{W$WyNVs#{qjD#Gg87PmL9Y6uxtE!U(Yjb`0K@YsR?!6CrQ z5;UHNXO)l0OM^#NLXCq0mQppEX-}Tz3LFTYxI8uQ%7XF<2wu==jKKsFzOQTh+eEXS6~UrkR36)`#ekQ!E`~q_(K8 z?gN49AppLO&^l7DV?BAQN3aKQUFw64a#z9Y$QK@>yad4Zx9=+>9W(Yjgx$KMK@;yu z&C=CP(47}4U2KwutLg;hL56xCYjV|VB3pPn`RH4jJ$5O4AJDmkmDnACk4AzpyJ7t8 zgYgAF!>3Z#e)%QiI;{I;!`MhnK8`;XAIwtt491V~84kn0t#JGeaB~gA(j0~#!(#*{ zrl)XO_*xtWWVo+84rZO67w%&io$*U<#=_;}_;XPX;wmf$*S}$veC6Z#zr$eSIun90 zX3fX(s~nu15Nm4%g1d@#i}E zUB|Ltci0)mU2V!DI9rnT1aN5G9hw*&O5W`O*9qo)=*zV1|DD-K^Bym*GK7 zF~;$`9d@*p7W29Z;kqktK92t&!rf<)9C=I|O(Zg}U~tGiB7`H@u30anEUUFqSiK== zFC%DCLpj|U*L_#u`0g`d&GOmKjWXuuhUIZy420PccYR@-hL~#nC9V#AO_5Gui{x=mSeYWJiOz0j6Aa|I60``|pn{Ww3Qi6xI60``} zP{GMTYt>(Ha`1wag9=U#DmXc);N+l!lY{=B#!KO)q|pn{Ww3Qi6xI60``|pn{Ww3Qi6xI60``qFwbtIHCfliHdPo+bY z3Qi993gthpbe7U;rDrK!sq{Rhmn;2}(p!}NK8ncLQCf&@l=&O1bhy&2 z(uGQENb#{*tn^%U-=Op+rFScpeKH8oF$s?ybKQdRIX>z3jxjmg&|C??T}+>6?I(PE zd>FECeEx-Q@cB~U^HX^@*e0Brp9X9mSbYhwc|QaFcBRjY4nB+`q1W#EZ$6NH{w^=O zzQaHKksnrPH-LZiu2RG5Q6B05d|>0VoqqAquG3m>+LEZH{=9jcA+w9JePI7yrlV_W z%S{ES_Wk`QpX&0wf7rrws85f&@E+LssHExY-Lo(}Z~wr?=R4ofy#UPnv_A6AeFez3 zkKbO%cid?FaMt;_(UT_Jb)pUYfy&_DlS+?FW0~OH?Rl@q>2}Az8xdL~9G|cEPovMF3{+Hj0qx1*Mc*Ww;c%vCGOgwG@KRUf&lmCAgO?w4jVBXCZL^}~!8NunF z7r#`Rc?r%CxA0g#<)&`N9}d4%IFIGiX-{s)m^4Xw* z|760=Y@~Y9?K7ID@PDLmLGgOWK!ubGJ)F#!iHJ|o;VJ^6LKXb zHT6rq;Er!2JA4}d#KfYV2rZ0vH|r)bVrY6K^Kj3Jh@BXbNk4!X!H>=pZe;ow^!w+p z2sbLtacOWLv8%+0F*tf(?*$KrDTK%JgCB=^4v*yrKMC_WHGLe@d-xuSH%-(q1dlu} z+>8<|c6-6&p_G|vjwFLdIF>J6Ys#k^dVsP| zF`alP0uk9@fFIU=3c{EUG;-kYK0n2uQOI3MfMfZ^%+?5>ZWKcu2f;{X3(F?b_hA3u z#kbf?(ZZkf8VMfjMzo@R|H7RJ0d^wPG5jeBewrwasBLhn)>yLaS_(fc!O*~h%YH)N z(-Z8<6C*~J71B4}iSPn_Kba_z>a8xTq3@!^m6DUw%6R8qaAx8mNoaXlF)RBlWn0Sj zVO`cJyQb{36jhrjld8qB{0)>@I0e0mgvEVTl}&|D1C|YcYvpV zgYtL#W&FqnOO=1XFT0BTGUXrjy%Y<F`3qVStAt$xtREJ3Tq6EJFU2OwN1*%=zDIAMsqV*j6_4p9RN{Vj zu+V2Gn5LKdKK@2I!LfY4J^moR-e@%R9GK)Av_D&~Z+*55pPMWFgQcH=WLb6rV*We2mb1U}7lxC4f4JQ)M~(d43|`LIVv^YJA0Xg*vcO>r@A&M*VC}^dCj~7_gMIHCf9whCE5{v%f0g4_j9WZzP9lXz#xBa;E5Nz8 z88%m(vA^0!&oopW>DD6dgJb2!3rmJrJT}H^@ekvfJDbuNaZgrL-lbl)(;?n8k-y2I`^^p8vaKQKaC8|eN8z3(pE{k4so4Qw;b_MPK5EfvvSi*i$?-I$qk zRix=5Sd20+Yi`9=fV=WI|L$Y_J5aTFxA?#N?XWLOKK_3L9A@F$!tWYBX%X&wu=4VK zhJ7VH9b;@h{@+tL8Q(ea8ID_#&6ssM9L!EXZtdh3)r|RZErbI$Gn-J3%TY*{gX3wI z7eiJrAOAlE>-;}3A@Dcr39!5JmS@8Flt(!xuO4=b1FM$;V7i7DHCE4Ofp_J#WIS&g z+$fL5F?sp;|2oK<34b$|RzCiJXC}@c$2=x4AOC+8@(?sKCT~4l-2B~}32}1f4=dV{ zynOthI@i0bkO)V4{N1b+M9fbii0cbxEZy7TfJe$9mzehvZkREk7A_zEKd`KS`2Vc^ zFbRVY1Q)&G%xWoqEUUF;SiLD|FSWF&v2=|$>bn26@PEFaq4A|09J3nFXMeA;s6CdG zW7g1w`v+1{AwqwSU0wewhc_S6ow>h&W$12y0k)w;hR==t3&XO(FnkrL8TwN#qZ#tY z;%8%ahUa_D@{R!|e=4XMhL0vNx_`k2?Hs!2>j;QOEHZGR?@A8jX|a)ZG=7s&J$y1q zV(7kMADbBBdDoN28t)yXXoGxqbbnEKJ}dGgL5JeS;IkpmK8;5`ib=7u!@NglgE479 z5FAxh-2$Mam1lj?{V1iZAM*2*p00Gc(l({nDCPUl@ZVJWL!}QZeOl?Sm2Oe`hSGPG z4q`bVr(9{J(gvlglzv(%=ivUcOzWellS7{OIhWtKCiC&OD zUg>O&s55#dxl%9lt)j=6BQzT zS}D)Ll7Cw%^?H&Yq;$B_B~yrRLVBPczY?$D6Ld_q|$jx7b#t#^gN}XReFQc zZz;V;>0?TtSK6iYO{D=Eu;gFq2&IQBovn0%(t4$5EB(CEuPFVt(tDM@p!5}`Z!0b2 zDk#cdrgWsz2}-9cJxOV;(q^R>D7{+g*OY!w>5r8@qx2=E|D|*w1`kaCD5WPTy-ewK zO5ac_dnORR2pu`&?M;d{wvW;S)xAM!i@LWf{j|E@uk>+se^Tjl>i&|_*OZoFP{Q;w zq)2b1(!36`helv38pEl2|w#)Rllev+>HkY0qRX-W#idoqp*>o6)TI_o|=W@_x2pv$t^p!miu=U+=%a zKjV9)U)u71rT}{%p8MVVUMRPq2DX zE{>kEEAsY;F?0+>!~62m3Zw&MS0KHJPdThC*y2mNR=_R}2Bg=+0mu0lg5avWhshNg zxsicW!S6!H*&w(oZ?iZilS@Gh`0)vTL64%~J0Pi|!v6>2zso;azZ`_-a{T-=i`vF{ z{@7aDtBcykoBgEJLvW(mqoxM8(~Xl@segmRsC>uQ;J^kMh7{D<8_<&H;H8r&V&afm zxck2M7)ZkVHvjIZCE(V^re)i?S>N=tV%QSBC$jjum`ADdPEu9+pGy6-a$f2PY&Jqk zdOb(QI-XfldbNLP;*wxPGSpE7xA3NtwV-i|;@sV0C54#bK&2GFk@)dJFnk6-b{y<9 z%rFey7$%Ef20zAO7{)QfCRU5zO8of9*r6=P&rB^Wbfe!I{OHH9X0~^!kq--}Bb?ff zkFw;hF-E7JwtbN`cfBeW8@Y4wDu?N2X-?LVnq~OMRDqhWKzJ0Lr81{*IK&cUir8x{g#_uZ}Unz?NtIUnA zrbUhUO$TpwdJ%_AI}T>eUw4^>a%@C+mV=*HmY4a;Uw7f&WDTTaJgkkU#o}KDdDj{PYVvMTJD)Snfe=t?p5STa z@B2zY#89ux6k|Xw%Du27sTT8UK)C*_&#XtdCoGa9k8<(oBl9u}pM~Q-aN!yc%gg7( zIzhUT8H{$tHg3l9XS`9@eOIi{@EOI%mojXK9NgTnJZy2X*%9Y6Vri5=NIso(yBt5R zZ{)9^IG=5xwBaI0{58w{BCs6&KA(Y#;8_sfDK=Uu+aUSLO4(PCKSt?Xr3;jvp>(BE zzLN}pq0%cz(Ri*_dKW3yZy#6s8>O!+{R=7L-$ROc99Hyi-!;DO?XhCCzUCa`Cw+X9 zOgZ|Y7cGhR0w$VsP%m2Gm3DFevWI=qD9-*~=GrX?^J+44=a%=YF5c`pjdrxKBFib#&U^=AAvS zh^G^*={?d&4|5!EKs=?OyWH~}b^{z-$0M_H*!_p_$0igU_EA3=2qqL9$YDaEgzW|s z3eGFwh`*RnB$G9u!43H1r;3(b>Rf~Q;|HGv@NxVd4xQ0Rc8;!bw|Q|aK$AaK?!e%HV?p-kd=(gu{K3lG zxS<2QYvi?o1IG~z-&Z((zbp={UI%_0*O@WD>2NSR{SE>1q6KpN`2FL!#>{PC?gYbf z^fp!w@TT)yY{t?$0S>Ob)6h#=y^vbH)WdFZD1#U2jG{%2)$>{4U3s;T$F7d~V+U;V z^2f^DQ_XL`8A~gFtlZ+{kNuO$%O5LqM`6ZW>N_qWg>rWoV+-3~jvTFi?F2!$Wmn5@vt~X=bU69 zeykVPJIiWq6jpBo9QZDnp`7lF>%J?-z-$|_aZ_jhbA5Njm=|LtG3+Zi#%237!+MK9 zlo;RY=ZvIfEsjaRdnf|&eIpjacGx3+22UB&f{tc5(5zA!(}L$aP4{Dz&Q&U7TDaFL zj|CP9hb#!aMCrApXbjgY{jt)AmHw0zavGIqS$TePi1Jbn{4vV%Qy$B^+)?h^=NNW! zRlnEGFGsz>L}S=8JcS@ziZgLzx8O`)=ldJ|%?Du|8*GAZU!3>zvkNwtynJOR#-nE> zFcwAjUd4()7v_BXC$UcMm0r80BvZbr+p+G*N4nmxx+rH1?B|bxM?Ea#;0wn*+{JP4 zg_(!De#`N2hU@W(&2&SS2NZh?pljG0;Ah^5+~36*x!9ZEiQavXo$1@fW8}P{@t^qw z!wQyH=_#>$-ZD^eu%R)#YYdI6K`=D_CAlIaUuNJ`@OJ2UEJ!po{vYvjhsOU&@1o#- z5Dbm~K>xe>XIeBgW*&5Ce8f`P=NC14{t@N0%E}aepCe-b ztkkE>x?qoQ(PhCq{9BD7^P8{~Fp7?zFNJldjh^vF0NZba^ z*ge>)or|M4EN6i^hw1;i^s2hH6@P`HbUV~yc@?6xtD?TG4c3PG)yrz?>g!vfs;Hv1 zVWqk>HZ5&-dFZ-oaQUUJbx;W9L}*!E-vlR!jD^(gj5TMD3D^$#4WOyQ&uwBHPteHg z3L1?m7|Ail9s)OxANvY724O6YhyinfSi(Q%$8V(B>BDpId{fMr-{Ek;r!6vE8@vtN zApH2rVL6yDj(aT*tlnqPU0y+p8uObC-j#RdfG|Gg72#*{*pXTsSaArm`|&Z?r(Aj0 zLmt1klt*`y$8oPKFAI6yjgQgXbL8FP9R0(=kvRrvX?P5d zaZ_h__lc3`laEc{*=RR(3#0~If40Eb1XzK#80-T$KDNQ2toMck!w>EF_X*^ghd;rbfO;!*KV_)-x6R< zi8<;1-HvIyS-)jmuJ8>5m3hWcUG~wFrEqHV^<@ovRQ)~dsIZPjGyIl-oYrD~^E{CAu zW|2!ME?U)n44nVJ{4>pGYBWsUhOV`=FOtn7M~MBTR2?>v%$9i7sXA;TnIra-cFAZI z?KF2Z`ZPwPt+RuYieS%*3sQ?NotXMUHfX8{N26Qs*T8EM85T1hhw{zmOp`DBs<2-k z3Ra&AY@x9yLvE*!AV0`1h7UYQma6W4F9hr7xEV7J&y2!8jnNso_c${OL$(I&I~$!D zH3Qlz7c{SKUEI*RHiKIgGvl+_iDyq3J8rahG{5R~ye=8Xb73uj_vD2$=N<1ACa`h0 zqHXP}_PTn|_STRtH`_;G)ETZt;p>t!?sS_}5`HxD;)B|k^96B@|4;@^{+qQISB956($VS6x+e z=8rpvI{E8u-1+|??>!R+N_qU{k2~K*!5evznEH4D~hc zq+YV!eg~n@LB_zB%jccpapC)*|qknAs$98ssm#cg$ZyeP|A%t^pAg)y@XIwtri@`+o#!7~OKWvey;)E4ymmMx z!h11yJmk*DoI6e%>(8m|VGpg_4=~R&OOm2u00VO!e%%I}jm;n$T=$EFR@e0ZP!GFWabrQ z;s!7*2kXGfF&}n|1FJV5%o<^}U3u%kuw0Z!YVzt~w>YqRPk|XliyEtEmd%w% zeWe`7Q67d&kvx8@TzPMS>26%Z@t!O11}A^`#6|Kh%ae!0CSC?^W-P7saB$^yIQhd` zMkMb`urm&yAI*5k`%7q*zx3npW~JhYKU@c$VvOU*=LxSC^I8t3yYl9*A6|-Z_gN%I z9@9paB6BAO-TlGWH{zjoH(rhesfF^*^u2zFOgg6T^+J1F!$<*ITJDOVn>y=_EhjcR z0$=B|reR;faoj$_g@4#yWmzHYqIF=$UOHZD;JdLaG4`IudY?uij(`lrZ~3%nZT=6C1e!#fb5MI<)Uy+G1JkBaRb&AKi}_tpjr$qvAD{w`VjD4=08lzRlqn z^jfJY^g5t^BRzQ-2J48UVFw`^20Z+Eaz#csRs|kjfR_i3>>MLJ{4Mcv@$gQ17X=T3 z01y8?{r5#gEGnK3I}P)I(dZJABj||nDBlu4M8{7`Eop1_{Nu%EehTV~u*kTS{wJYJ z1(_p47wz#bnYn)yA1pcYqmfK<0kc599~Z;6)39)~=-g55Ggy5*Z+2C%M@6tUb=t(# z_s3s~x5nQZ2WRz}VNL9ZU-*Rn3>#-y>nD9~@|x3X8z)_;l6Qx?BD@PPIJ1>b(qk-Ho$Wz|WP({hgM--qt03JirVEtFg55*Cp3E z`Ri?6vK;b;nGndNl|QavIL;m~>FjPidn>|0L<@+Ra}lmT*j>%xhaxoZesIc!o%4IMC+2$ zo9plupDOkhyJB6kmg(RF+NzWvN%EH{Wgay3#+7 z8*<5wAwr^2W4#fmppaZ33bj|yc8{x_e=6>9)b=k0xDXxsMM;15-n}1+9{*7 zRcog~n3h^vbvyTbqCrB8s-!x%<4fA`v4;FS%@xprR@rC18!h7-3G1Wr+q?M>fjYe3`Kl!t4nn7uD%`#)eYJS$%< z;91E*foHvqwqyh4uz_cN!gItwI;i^XI~aW78Dhjt$uAJnv7X}pM>@57m^s3u zJ9Dhj#{tBdg9fxSKAI!{)bIU^Pw zP+RqfFs+0pmxh#K>YLFu?N0vybBk*_<^Dr2vM_%>ck3HIIGgLUogbPvJ(M3kOmp>n zGOK@Fg4aO*l(e_CwbYfjw_#c6fEB-JIgAAP*RWXWq!0dZj=bR2+23Li87I z48oW=Fv?z~>uFGB{Ce}VXTpO|h*2ioxo`;bcbj18fE)S6Bxsg{`RdKjz6S!k;BJ(m zR|tn7z2X4Bm<&HdZz0Sk4r%bn@KH3VGWB;A>_K|mPn8b0{`gr|WMBMUZ+@2iG0R7K zN8)Ec26|D1G0NnMI&`@es&rvVj%o1*Mas`-rn=G4*_H_vOr6hIut(9}GxX7{?=p5VQr1!_WVq5-z zdDj@GU7`GWSnkUY-T`K%d-s?9^A0e*VrvoxpA&}J_Aoas5&U9$mGUAufgPT8zW{&EYQ2`2VpV%^*DHkq!W-q4`2}eoz z3NJ&U$mf{omA8stSgs{=u3`nb-fdo_8@>;J_d`nso@VhjFFGCfef-U14LenlcVTv_ z>B2NV$20=Bd9g#e>GW)x0ON5dz@O7V3mm!4%lV2%#C)VjRlJ()EB7bDOXTK!rdAE5v-|P7>)N7F#mRGWz2NdgKp-b}(=(OFi7Ikn^L=+gZHCZC>->?o4+6MT#5B zoI_t@4QHOtb}`n}%u^URahq2b>*joilZCv^OAeY%+~&1_fu}kwE^qTCyd2t%lxkPqzD;U3Iy^MiBl%xMglxXtTYT2pMV_IaBZ z(-{)V#s3M<;SUp5R+@v`y#9;X9&Xc(WL{;w5o#Tjc^5M^(%vDyxXp_OY8f?fo7YP$ zMDmHh$0y592e)};lG^1C-yNjJQ`ej;w8lc2owUw(7@D_v@!QL}(&11pw|Oyc;x;dK zTj$e`E7e<^d60n@I5$g9=4TG0^=gN&E@yac=5vg@P_50GBN?|utxGaTGE}LPB2|mq zyq3^j=A0~c+~!rv90YFjk^_z_B)Z(@HH-e0YR7F}Z!>#X|65lxo9mUSB}9;WjU3z<0)NUS|gFxXo)e?E4+u=Ea#fr^%t6)w-0{W~W@N zxXp_O4qQSWw|TKkvI*3l#ph^0y6Q=MR?$o^qY_U#pPBrO!!)h%2K@Uu!EIiAdhB6* zywPdsIV8j<=w$X@|2D4|#r-+_hxg!*eM|erDZC!pUXCo;gKa)9RAZoVTNUP=FoMa@ zRcxKQnMFCxWlEUN8bh0v$^T<9Vb{+LkRDK)*Lq#+;Uus}IpxPs0h#eLmrJ3h{L7_a z)(fRz@}~sRS{M!Bf>y|@%JXiBI`aGPsgaB|l()9DH7%}aHN|VmpM*<^u-O`AL$$RvEh?!mgUmm08C6Ne zwQZ;n1d|##^e&&qx~6hmgXG`NBPo~!dR%40Tb~;0K+22ZUxt*pF-R^{Vz9-${-@_0 zEN)vWYpFp>bq&q!Emd+pYDrmZMWX&2s}@U}pq?Ar>)E8BRM*Ny4E_Xe39ivuQiqiX zCDnEHXe-%c4$?fC7qqLJn_4jo{K2PrORCwmdH)eQc2m11jx(~oT-0K(QsQh_nF>a) zD62G`wZyweNIF<~py2-OF&8cLu%MTDjoQ>$!V;B8<@B+xfek24XRa(N#}+E9#JkbV z%SlaHLqnPLNtk+FFofbDZ(c94&@OCh-p^E321oBIBFn!!3Ab7WZVYNDZwWj~hhAh< z*IH7sq|7UAud-EOqgNvyVqERy-}W@Mu7=A!nH3uz8ug<=hwJGaOzhhpe$KgU z8Z-S%p&J7Z#yA!3#tftNA}oB%8D;#)Zw8#Lbd=+LWP;@&r(?_a}i6{vz#X@@@{+DFH+>W z=CoHR%2nO4e^KqczMTHgD88u3y|r{tMOP-Ctay&%bj8_|PQ`VKk1IZ_ z_yfgP72i~RUonhFnEA+3%u_sHafad*iq(p8H9O*cPVEmXeogU5ioa0Yr}!tu6wbRK z{z;0X6elWPs5notQn6KWh2p)6UsQZbajW9Xid~AKu$OMW;xxsjimMdgQhbkyKJ^F1 zGz`Sd_v!4%aOeAQfM;Rj`>w=4Nw7be+r)7L-}|tx51CuX6r6+IjC}|e^sXW*qjG!rRuk1i&glaMBw&b#U-Ga*y^Xk>AHt#ix9I=8@9jZ=Frd;3_= zog1h1+PN`_eGdH$Z*cTF%%kuR3XN;o+~j4XbO+$5QaX;ykkav5DM;xCBao5OHG@>& zQo7mjaStP<^J0Z2B1k|=_c#(ftdwpO5>+W3Pk87{N=NCTO6k50Bc*gAPU%}pw-rH* zBo6X2j-)EHOO3pP6pU@)Qo3|dq?B$1WBXD%-tX#6$U)gC8yrhNUrHyF%D$8?i+S*+ zbpOYY6TNsDkrGnzrF7)&9bZcK5dC^e>25(}kKKrej#B1?lHe1~97al)!;nAl z8lot2Gei1Px=xnQm(tx$+Xt4?QT+S2C8guKCy~-!!pcoZ=|qTTaF^i4?VirFR! zDV<1T5>mP^vj_<(oiNV{DIK58pp@?4Nh=_w`#e()Na=1Mwf?1aBC-fb>7D`^kzKuY%v;|HX4%b8q2O7|M$A4y7gC5rk%q;&HAmypt3#QYqMl#b7;kj<{oD@bKb)!wujtlxXQWB zy(JV7*-Zwg`dc&uX38nh!}JMNHZ-~~6dFYpX&7~EM0%X4r?4j>+4D0wa2ek}q;&2t zB#{&0iOBqpoow!(Lb*oDwyG+lB6owx&=bm4G+}SvMeqE^iRLU=ES*ErDB5F)Y~<9Q2+rh19f||3}me5Vd-M^1W>Xl<(~xpnNYCayuj68-koL zt9{A$CZoTM^Rg=c!8Qmu_h_4AA>U(LCREQ{{UzjitRt%F(3IzK?U+%lx2UlIX+N`< zUFCc7-hzZL!I*;ilQmKeW%XF?0Zz4{szJ|MAswE(6eXfvDXTjbbL@O>k7jQ><8%Mc zClxt{&@Qq*pvd}wwd%f7@w18|>x2JBwSP zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{z;3)a*{^@6cz}raI$uDy%KsUbQ^R%bS3lx=(*5E(9@wOL+3+}fgS;!4V?j<3LSzz0OspmXg*JS zpx=Og1-bxrcqF|8VYUq*TwgSv(|CNgpMc%~y$*UU^lIpp&`Y7)pzEOxjmm&|LBMSM ziF4sz1U(&kGIT!l80Znu+0Yr#sn8})C}1{jec=Jn<~AqJ({i@^4d_>(cR+80rYvGJ z^b^nTrK|klL$C=1??l$+fQ8xU<`1N>= z6nD8+hs`rUIuM3uf~*7Lj^wxQ2mTJY7WiA>8sKk$tAYOmTm{5AB5$n(?gcIbb_16J zyMRl8yMS%LUjdtezW~+){|i_Pd<|F${5h}`_*38l;LE`Iz?Xn?fjY8n_6!3RnkR39JDw z16Bc-0xN(^fMvioU0169%#yZ$un>~jG z0#FJlz!9Ya8z1s6I8TBbyxyT(vbzirPEPVp{F!X~=TtIP`)WzrKG)7cC>&I|f$%8_ zgCl@!ey!Q(d2#H)3j za4)Opj*IIK_2b?TiPhTP+}zZ{Sl}NU7d195Zj3g!G_^J1#B-)`X;o`$Sxr^6t*I%> z9VK*|Ue(%G*I3q8*VGv0J&XtvZK?W9dtHl=G3g(gIO0z8aw$uRurrZN$x zH5yC;d9H3-T-H(*tte}61xc+<$!9q#i1epuSxv!^afI!WpO&g>Mu%Hj;~8zy)~d$J zXjwI~6K(YxtF^Wrn`OLm6l*<8LzG=xR#z{@HTBojT-D-LGU~apsd21F0^_ZTx3~o@ z5|y^BZZb(E@>yNhP*;Cl&t{I6U+1@P)C*wEY>HwlMm00p%vuX%#k$PT1pAoZTcXI4 z$&NJ_eWRhQ@wzDP87gn8ud9gSPRd2C)`i$^6h*$8%i3zAyn-^&i>9NqV2=r|G9ulo zhUT{Gn2&}gGy%6Mao+4g>=2Vq#voWmTwm2tg{Ea!W&KLTB~=yCvS@29nywPbmba9F zWTg+UhI1f4egC}6<$a29Otw&a0^^igP`ADTh-E5*IK16m)EtlwU^aL zE9zP*+Uw+Tt7&SivSwGc%KcJ&Y+9-sn%dB}(I)5+s?%ks02c-0eas*YAxwQ|srM*#B_va!kQWY`*p95c`8d$6>#>%cy>rD;(W2AsO) zszkr_x+~(BRgP_H#3REVs6B!CsIRZ8L4C>gDcD&~c~I)Bs@wP^R90PES5f8lcNr+X z)aPQCp+~B`y%mi+Q_9T!XJr_tTTHY>KJdghYVY@-se~IluGc%6r$+3&ti#}vD4KO~ zE2`A%`*^TSdI<;SyS<{;Pj^~NQ)}y3FZ1$ffUZ2V*j(4z){3@ZvG@d=_QW`Wf=p!t zDHVA613&w&z7MS8s+NX2JQz|tjdk+SviwzzP3<+cUi~3CX-_`$c<1mzlII@zL`SQ~ zi=|PX2WgGQ_J;DR7V82&@EGhG$^!knr3Tr@dn53Y>RDEn!Q>2emv@=}Y+THDh4(C* zN529yn$pIsE6ta0m%M;lqE@|n#*mKOd1ilGq=oOYF+Hz* z+IL}MPcdodVPz7B8g_D?}A4Sg6s*w$DUPdZhdJxv8*;+jMzj-?-fT;LXi{z3ZL(++!H* zgdz4sDE4Jc_4#U2miMj1Jy~R284go~hsYq8>9+~IL@p*Q8ez$AafaIEI@{%;tI@Qd%TKt5meHJxYSQ~Im{-n zFj^ZC2tj>i{BDFxklvetavTRgQ;z#!HgQPg`=A%4L6z~l2lgPn{ek=$86WHCOVErH zgQnUGdR!=I6phrI6pgz()SJ&Rb`#|pIJkkmh@V8bkC{L~6AmIQhV#{x2=@cumI%l2 z-XQIHJ_lJP=SBc>)*|Z#S(dT}>80ZN2+G||x*nCpf6)C{$SM;lv-~OYE=#!Caz-Mu zuFAWgf%BoB^8DX5ozSmM&-a6$Kgd={C>PSqpu*k9z6-}B$~rj?8O3_{U#Y>}ir!nP zr046mXFi%0$vM!zL~*I&GR2jOs}xr&u2Ed8c)#L0#SX;{iW?Q5P~4=rS@CJb=M*~? zw<&H{+@ZKr@fF2a72i;dEACNzQ*oc-e#Lhc-&Z`K$nM8_;Vc+YWV*mqwWlj`PLuvw ziX2~Pk1873)hM-(QH&|(D{@|x@g^%4C{9*C?)4ykBvhVu#`e#f^$jC~i{RtoXFzbBdjc+Z4Ae?oiyR z_=@7Iif<^!756BJ*qfDk#pM&H%2k0 zn6JorZ~9MGEKr=TI76{Wu~>1g;(WygilvH`inWUMip`2`ic1tZ-_Cq2Q(URIN^!N~ z8pXAW_baYb>`>gGxKZ&5#Z8Kv6`xjoPO(#Qo8orG9f~^@Ur~Hj@eRee;vU5}756Fb zSA19TeZ>QcWEfcwAw^kB0DG$1(-kR$U^rQ00L)f;8M-Xod&I6elW@SEl;~ibaZZ6iXF3fy{86$R%E@xSWV*=vKu$ z)%{-;MO1Mbo8 z`)jTnu!}o(Iiq?h(e`;6X4`K1_LWX~6*Y2`9D&hs*|DvqxBV0uSKMdv%DuFu$|WW9&5`h48}`Hz{Kp`4{PVaVx&YLhZ#~L);gp_J84Tc@HlJ` z1sw+py`@BySHSCSgn*QYe0MlGi42{0MZ_Sxi*aMVyol-JUNih#_Lv<07bg!QjWJi! zGB1q%Dr4GcynOI42;;K?&x{kM)9g3#kA;&K@Q#IZ^X$T+ zq7~ldzNskTK`6+5mZqg1*W>O~*3|U0RM}2)t8|tk-Jb6eT=XCvj zPA1_R;mWG=b{x9b0x?W*I;pw6oRXQ=pd03=8*Av~#X)shxfPHza0Weh%Ir&XA&>Z= zmq&0O0S~Z@&+KOGa=~tAI%Ds-qdk}y;8>mk&G-z5*ViF)N40PZDu$)%J$H1ta5026 zaTt%`meZih_)Ue~nCbNvG@mr1j33{J!8wh0VR;&EEC=6FrW_cude9^P{4(5(GW2@S z9X*la)dT6VJ`BBbm`xngScSo03=OJGJuiekn7_@SXX+V7pC09KL3*1(FGpz+j9(MX zL3-N)`Q!Ly@^@Px4vf}MK`%;!DnoAt>_K`v0{KgWpP^^wnAm<)?}6TFh6wC5Qa_>y z5S0M}lZ*vuqC5sO=4P6W7uVKap73OIi2i8 zaEU##mp$;h^JaO1gT0Y@TJ;g2!?o(dKZ37g&K)oyY#Z{We9nl%Z^BdFv%t}G z2eSVWg&zhAKMWLp7+9qK!VkkyW-7?tBFW=jUpH3Fr5vGGQR@*R`r*B zqVGsP5l_m4KZB+`sDf&WW6cF@{qe=)#+@_XyTqdZHMN_)2gc`%PsS-?ynijDb4|Bv zWp`Qej$KwkM;GRvvD|1u*R>y8{p9?d_1&rIH$PdKUi9ca<2vJ8&hOaOIldz9j$7er zd)6-0xgUO1U*RVN!aK8ep*d5sVh?v`M<4E95d#lCenmWFt%&DfT_$D^Ux)(F!8Pv3 z0r9N8kOCYJyA4EpK4@7TUFM#*ARQZd57OFq@Zjs8{!~ZTpxBDtu2sAXz5Iniv1z*< zwd0Y0Y%1)hs~tl{%HD$qpS|i+1-r*H9M%|j9Xxo4jr4Y7EgorQYS=&EKIF{Uw4cCj z>#*30pPU$5fo1l^yME65<`Dj82%nDpz6^TD1JUHKXJhTM4Vo{4CRVp~ab4$WxBSnn z{g>fPqmGVUBk+CVSW|XqS$B5Zn@}&yaOd3`v`KSEm(}@dH``>SwO|-_r-Sc$)H2U*f8_+Mg zrukUuVx-5l=+B{}@%_tmpMbdy?~=)IkK_3c=4C$IgLax%BYZRCAndXvZ(fG77}|G2 z@56)DJC3n$g5MgXvkF+Mntq&{pgYZ#K$Dj^%sbSbYZ&RjK#^-7v(;RHdp3liN-o! zo*e7g>K5F$chxoPyRCxC-3)hMMrV9m3GSS+mc=`rsyN;1&t2brUvx|S-fhpvt@NAY zop@Ha;92E!9f~fGyD%4>yuLfxx+DJl$SOR)GbJATRucLQ`xn-Prr66??Jh^(G=1z+ z$9gbJ^E@%O!imLJ3`OyL|1;q~IM&gX68rvM(qjMnAH$@GehoTR3vy4GM3vSEcPj`kvT}d9oaF1BC zyGSnw&t@^|&(OaVVO~MrvN|6^JuKLTQ$8LHqihkh#}w2x!~UNBar!UZ^?UdawmNog z{`0~A$%@^+J6v$@n`}F~bK1~BogHpy^!o0!SbaPNWw>YBmbisy-9_DRMcq%ysfgRr zj;)xBeVt`_CCkez>xlV%lLx#)SUV_#8M85)-Q^x%5Pt!FG^6{zo`E{M_Plsk1mm7-&Bl8m{?wQ-*3Y9XYsNh<n6`TQjMKz!o*?wu#D@2*5YNJV-K z=cacY{|v%yKf~*nk@T84`jUT++t+OfxH$GT-xj|d;Wi}m8R+OTeG_#nebdcZvp1Cf zlf8DqN{pd%yHe7u!78QSL~UAAWk?QWs{UF)8`^kW*Rn_X6M z2k#tx%%qpnd0U+Q>Ncdy_*uQ1<(2Ga5MGxugGJ1Anj9-uQsGbH)q_My{u2R z`FH+&@b&q~m!b80ROiV7YXBssSzY%r70`J@d)tnddV(4&y#~wVVPO zKYoF@rvJ?k#4XUcZ{eLJw{3}UW1rn!9#0#0msj3h(=is$>$)9b zh;mmsWN_-gRwBB63<9R0o)5#IvE9YpXtmU zj`vV!HPXHuyx5)H8J!PxPe<5k4EV%M+!eDGYnf4j2d zcXl86f2fxjpT{}7{sr~2Z79NPy(~e!7+zp8{Px4oj15d91OD;T6VLWHSdUhs9?5&6 z|9%lX@KYTBZ$!E4ccbpopBFwr>Mp)w_iE5!{jI`2D5tX$yl{t2`po}I`W4^#Ci4#- z^{rWdKKK~his7$4Z{?Gb*or5sV$&amOusADYK&W*=3Bw@Kd@_kw+Zv`@GbGJY?Huy z!LjG;!a5XuD|q2|3OV%<3OgVZhY9XfV#+>*A*DT*&RKEWgWH`;*t&PE&FPcxpUZ1kni z!`%BFi2nHUpyG~~urKI!?g2W)zo6sgv+-?ZFkhRt4DbDA!q+|BxxRZ{d`tXz(7ZNnrSIO6j(t(l zt#P(T=McPa$Ct(@BTvD&OAFR_ukG9tPwVAg8C~CfPv_RS#M@mO=YFVQyy+V6cfHaa z6G#66KPlruk7uhOeg_b`#q{^_dJ_Gvk z#~wR#3=rRNqDt|f4?7i}JDD%zcXAYZ4D=EA=p62xRwIlV!bE`)m? zz&g0+$*Ub3!%tACla}O(ZFCpVke<7M(iuq)PwlaJ=y*iq zL9ZHZD53?d!;t*26Zm<*p*@SExS+h>Da7U8gfTBjI9K z!^sGWIM3kzbOHn7od zHqx_&!?FPEBdGoMO+M9=?L{X0fQa zI}OL10%xb)K zCAS?iucP z))X(TD~DW3Vi$QKuNYcq*)ufVVOx;uYS;T_9>RLE|KPBq?72hRuD9$zI+B^otqe{O zQw!dZ;-1mo=T-ABT`fbJ6e72o|5 zxP$4@G+x&{da?h@v(2)G)gTn72G&A*xw+jdS+SLI6a1F=6!tk%H*n{Lcx&kX5dQ2` zS8_kk%6J{_8{ssxv8u+hf8(WbxkvSF&o*z!zccs06`ON+hL@dpJkN#Ju>S=K&W>=l z#EbYNlby_+KIyM`;pck9Hk#OI_g{i~gs0-NRkcH%0QSRjmYl|pU`L7o)Z#hF8pO;G zd4`#C>WPeV=a?~Hht)P(&`19#TtRWLgadY1X4WJ4|maJ6!Ce zkUk%syf%1y8fVzOsbkqXDU-+;;wWgYtgNcet+h~ra<>$WtWdq_%)JjEfHPuFewKS` zczet(E5zQ)eD@UROxvw0bWaISCZzq$J*oQP8aE~PUoVMO#>UOO#r;p$*=*ZxYN31a z^4hvf#&?X#%X3C$xY688+&|q|SbK?kR<1jxu*UtIo$Ee#T%-`3y5W*L~iCVf~7+wPW4UxhvcY3v1oc)$YwVzH?1Y`1CQc*thfC*|Kb$(xaBN&QMHHPaN+aan+mT9 zpXa`D144Y(3Gbz`IqrMdfcn;r)$XR7s#jd>))lT8>;BLw z%qw!QDa>=sY)(>*c9jN|rvzq9 zKUyXX!{tLyhMoak49y{M0dysFJ#-uNGU!#%YoRgji6#xwA`Q9`JD@j0Z-RardK)xH zp;w`)C>tBuIx_dn%8|L_kpTbC^^DfYX(O*2JF;QyNV-cH%PPd(TG$$BU07A#A_m02 zw5+9~7C5E3rH=W#6c?}+wqqk4RAIYpAGD^l*Rw`_J918xrG=jp zx5pR&#c4jSiZsK7IA-JD^MRwDj7=rPbUppD|YcQZ8O;5*J&+}6)2{sjnI9|%v; zK_y<0%nS2Rnw`*Cl_Rtb0lq|8mRI4mUc1mZ#-J?&DqPDzfuy>*ENK9v#)m&th< zM60$}w1LeoX~5NSb+{Og*W%$C$7}eQm|OCCB!6gzG;|+ZxR_2|V|9~P5g_N?W!S(> zl*oBw2p|{nF$cJEjw$ky@h*Mj%Lr%G9^Ffhk+~$vyxb(23M_hQ$G9QYAUW{M=G9p( zHiye$p(Z9T-n+gLuC-QuRavto!A$*GdK)Hl&uq)3k5W38L~KCbp}1J?>NC}$)&6(8 z`lv;=vtg0ZN4c%-FR)Ch&pmFzfyxYDz1J!27>V~5JhSkmW2lt(B;R9j7CYaaR68+f z1Qo+lodwPC{e>f+YU02s^mv%MQN}nG4#rHsKS8*_k=ZEYcOD!9@KX7W?%0flYdl--}v?0Mi2*COukjP?SLU+jf%2s~EdU7q8SF>E@5MtSH== zF2{3Ij=NwEBx7Y@){$%7j52H_|&E+T`!wVK#AKwB~@`n>460e&2rV9JsEEKf`*;^AGtA;Q8}>x1j%Ie{Az3inl9Ij0wx~vd1Ts zD?K)7AC|DdPQ`7C+ZA^x?o@n5@m0k)6yu6}6yH?br?_A7UB&km z4=CcHlKSD_H-t0x>S&YOiMV`+{dsLBUHqy>B+=x8Kkr-3VSDd6cS+PKI zy5bDQBE@3Gxr*}@Imerg7s*;82EEP1C_V~_IqcBD(=n5&xJ|J+$#cISOiec4G5o+| z{0w~LWe>;4ClPdb4j$t#!^@aBmmhK?5Dp)6#5aj>=Z7J~uO{Lhvq5pYVqB3+Ug@8u zI34JK<_yKTit`m05RrB%@#FZ|RQFoNX2mul!Yxr;s<=%3S1PVjT&=i9ajoM0isbbi z)Denz_;s(~9Z+?a1LXUKn6LOr#i@!HD;6t?AOqpa&oSO2#Tyh?Dt=b+9>tA{-&EYD z_!Gsr;@gS`6jSgDVmeuha^Vo{&1#o3nqj|F?Xu4m_6=%(Qn6F zF$re6^QJ!Hctts@8Rn_9L(fz93Pm}q8R2hI`$I(NM-=4-EckzqcD(d|NQC`0#l4Du zRR4o&=fXLLKaq&=XR19{}GWdxg`$i#6n*FW-FEu(XVe*`)AeuCAB}P_8n@MGoulHuC_yr?T_b6`X!LO zDeaRL3lyg-&QL5;l>UnFbJadyae*RvD$-k~xKeSI;%db;ifa`!h+yi-pR-=Zs9o|4 zyF7pRGnlU7`1Yr!qQS4vjXIJc{Zmw@ZQ)10;5!Mr3kyFJ7g`7o3++l&%pJje}wG6PuYB0Byt`I zyCWrc>VC#Vp*nkR)LvH@*=6*>27F!T4BOlA6h$%*!joUMwzgIGrNws)AGaaj||TACN^{WvzhFnJuH39 z&dOBgq0_lXH{I%W08Y5d09RpE@-yY{c82qBaDQd6w_z+6)qCEu5@Q>OD!!jMpfDWm zBh`*EUeq>412Xu)auzhh_ZKdPFeVO+BG0>>235vyD(uEguS;R!(`A(LI}Z+njU%ug z<=6ydIrt7W<(Lh#i36jYMg40UR2jdSum|a75Txrbht5pe4QA2tuP1am4Y5VZ+vC^n&1+ommkPqD*Oz++h8_vNaRM)i_)OV_^p6F zNN;i=fA}Qu>2dCiaoB!T4}%`>K{1L(>PHlf`(|W_yq8fX%42WT1S`I{*@SerQ|c&~)!T|a#0`u%=HN0skK;OPtx9IY5roTzw# zVv*t;#j6y{6l)dv9Fg8K#d{SWCZdBps`xu~?^HXV#{u3!Qjaa81Lwmhj?a&Z>VLmv zJ^D73&xdECC2*Q;7w;j#nr`92(emK6XYWSP6g#$a4|!-i_TnD&7q~CjE&Md%aBZ*^ zdok05IUxJ<%Hp<#`%K~w?fKZjyj_^R5dQm}zb1cey^S(@Pp-#Mvn8n(LrcGRzD3g@ z=c|OUa}fXx^BUg z8EWRpFw|!WuM8fV4E0&UEzj~8>a&DJo=t{2CV>``p&lneipfxqmjL*H{4db^d*Gqb z%@5B*=iBb}xp{fxYgeA>4$jTHhRAc#;U0as=%-XuHShBY(ByNaSot_C>z8sPdtMy$o!Ls2uz=9C#K{>l6*p zkLnEQFm!+6#vqJ|1Ebulay<>IjNf^%8#BGgbMa|0%J`iNhaexf1D1_&8wSnS3d_NK z&4e~_U=(@JuV_$Z{0dUzRKI15B>(iaW32_lLeypHuZcq zv(ey4{)W<4)(h*MWktHa+6;#v&u8fQ0n`w5@1GA2=7i}5Q>2}_Nhnu(YS5l+9hnaf zwixO44BfxHnKhA6ls_9jxE9XjgFU`i-V~nfmE0fvI9~ic@VTALCz|1d@V8ZsK6~5$ zJo7md#}Aah&F_Q1b*=O+oLk`S(~GX@Mt0u{MK^UH%wN9So-}Q@JsDcg(Ac``ru?m4 zH%)?`3?19rMcx;C=P?hxQ{vgN{lGb0hSw#J+vmKoP8M_CSf`K^XX<&|xCRC1k&^=( zs(Ku**T>9p@HD*_SohF)>g2I06|;B7i(HLlHc3@6NRmt07*5ms32n&+I$*<*d87bG zSkUd<_r)<3ss<)qi9eSW0CxD>fN=643|@MqUh?;0wErLfK52W5m1ULEdUY6_j%9yB zEX9NQ60oO>W!B(-qvZmLT5N?vm%yqg>OBe5-}KBcI`83`*6)F5AD18Aygr6!!e*oN z=2dAosuYHkpuIMH5vG;U=BlTQtHHS;FTLC<5qHgDRsUQhJ@=|^=2 zG+BfG!u94;NpC5%QHEY`K9zHCd}53;;d=9_Yw!x+gh2iAsoYievLOaE^a^1PasdSx z`dF_1_*AaPi6Wd)rv7^KsWU*2Tta_*D%a=qHIKLmafA7rAIKlM6{gGlu7EaWg3Fh85#(mN`7`AeCiDdHxNE`DJubm?2k`fippdd zqfEURyTPFQvEWnD3;`8PozGaX2hjfE`Bb(wRVlR3r^?$w*;KruJ}8em8niff8dEfL zuza@Zet}|<;v7XjGxV=lY*oBo@eak$6Vb09Q2d&@Kdbm%b!YnybS`$We;ahPe_nOs zIg^f-SLK=rE1i7iEN?!xvtt)pHDwgeEU<94+`)8vw;O$FZxrhvY-=cZ+8vp8`T+_5 zznFVX%(?g9rlM|#*T9K z(d^?DCK6p&IMoVn_QD=nseE0bFxGajE6mHEfjy;p*9>Gm0&A*B=T%RVGULzs88qJ* zCHiuPn1z69N&+@H~fBBHo=YMU>%rp%!b*- zfzjgKB|o4+mGPSidyrlS2X(lS9?{S%huOq|QSP)KMT06+&m6FW^frQCI^0N)1G}Ne z_jZsT*O>Rk)AnA2@N^)5U;})5xAmg;2hfYc%_x)B3OEGw*BQtk-i|&!wh`m7{iset zLx&9!*lDDGL=hk=2LznmVWLZrgg;6#!52rzup=ay{gYNzFw80dZ9#c=KL+4t})9QtB(bL#B63Uj{ zJJWrKU~WjiXXuY&O#U<-}DA$pAB%&&t6Wa;{yuLV=h z{_X%rE5;Os7lu2Zb^2eRSfnVtFx*Sk&X+C2U8i`9;u<14$y&vy6u+hT10v!*sdhf2 zWa_fiF6F?VLDmE5q3n$nW!Ny>&@7X;{jCq4Fd=`yyl{3h5<&OP7Z`^3# zI8*MWy$3BX4XjDtOPZt~wU&W)X?*__?;Koa|7dydqnZyk$7lsOZ?+|QZ=VrQ^^Vv& z?ht-F2M?Gxnt1&9(^C6rX_kU!!;fDBGx+fZ0Py24(3WiAOR#|-|E=eE2>`RlZ;4}Y z_W0-YNOsv7!HLhK?WgD9KlwzYBpkCn!OnpL6*I!w<8ff2OKHYxi1Efei&wJbvDe#k z2fqfO^7!+KBw$}*4c-srdn)KW#~Pf3w2I*z5pMo`NiMaymxFa2PyceyD1|#Xd95Tk z9TWEcD3&819Ty$^Q1GAYL*}*3mxB!T`sFa*&dVFy7=AqOqVL=i{*-(=@SPky<0Bh( zqu7|!)3WZeS2?%2w}x&>S~*DGfnLuWiHco`5P{J}B9emkO|kRO^iP_zphrMYf-ZoL zL38wAtB-*;ieczh0L_t+LjwCP;iA>6slL*$0mQ(efAgvI#a|kN4w6)25vp9fd_LqoWgY^KIs;nGmuMLAS3EmUuPQUbGYmft$ zEVuQ#hPJYDU|WlawZ`;U|H}DGoEFjdH_y|i`vF60?)}Zf{e)rokw?VCCgErwsdmG~ z?Snn27?!H{H_ze1@o6$~7?0r|qd}GNn+m%z)9XH1I0_hLjJ>~kc*xCm1Y$YJo3Okr zNAGW*Z)4c#i(c<V;_NmBVcE0HYO01qDY6Q_sD>dG>)`I>Ix5DbR+V|2Bmi zRXRLQ_l)vy((3)qb0CmE7=3!T!EEA?z~10Y-(u^O^kh_040#aev$z zV*wg^8`Ny-0*u)qcTY3y*8_22wEmRT_qP%IE~>VfVW=jcQ3vvEGz;O_4~#PTBckbj zwHb{NgClrG|C|bzpLJsTJOguK>sT688G6QUFz9|PzKw>Mgat9<3*79x1#|P4`8M*C zf`NA)vtLuO-T1APBR21k^%{JI2S1q1n+@j%1m?cHvN$LdpXGkPo6uq9y9qd&;eqUj zMEPz4%6Aj+0(CD^oTGS^V!a}tOUA!m@eak$6VZ_#Q2ag-&(1Fu-&5qNo^+Rezn~*$ z#v)z@{n7Wbh_Flf@Mn;9mH3|eAp0$21OA@k9i>qI_Y~K8a~%xVc{`9RaIH78ezNru zu7}~;aC}ppYsX&NOtY}ZQ`d(d({H83HxZjB#Rx3| zatgX1vRa&6AJr&myB2@utLQYC@m0jR8+;YLNL#W2b~bz!{oZrLKf1U|Dc%;Zpp-&Z zYbU!LID4+F`5y-T9{(=#1s2SXej81@l4kGQXj(PR@@+JY^YQiqt_`0jAr_{vPx5() z@C(SkO6V3Nzuvl=*WvTGzgisQ$dPtj{@k+NrrcaNEqC1b@RM%j;b-y|_4#eWx-g2B z+4oxUJK*E7v~3c&ukR7YHtJY0?H|E+25T|#N#TDJh#*7ec#e8|rdi*E5BFm)-1tWj zY>;>7o1O0ps(k2RF9XXZitk>AGki>tA7TLdQJn!DhVC!i7=$r#U=;o;n6B~TJJy&P zZUHRs(4flroeO)A_e{sYO1@zjG+#|D2lF)(+Qfm;nu+%%-?K&;zd|?!>1_u+#wR_z zb9{OWVK#AKlrv&R(V)uIABX24y`7-P_cH0x-QKM`YLa4c?Itf`=*H$pvSb)q`rEejRwaI0QpSu|^K^gZY;!l4Cz954Fip;MKI299QU zAo~qbcxd2xY8M_F_9C^uE%LK z@`or?e=>;ilg=6N-bfvEd+^LWpTvsK-i`ZJaMxmoeKz<;JGL74`wZ&>`**U7dv#L5 z&*F}nT_@p=8!NgFYjy6%-IKF-;d=>dau)7BlRPl^?18Q~58)mreCr5poK3PDy%coU zfbLqM`)jU8M}BxtiXEQ`xyOyj1NrPXx~=F--45=}f#qcE49A^fGk3#`UvZb+xtjbr zjw`xR&QQUgOoZ{`;FQd`ITHnQoLzr9+cd3+{t>PZ=d7qf&!@!&EDFJ_B*uHm`mEAnstloNT_4iO6A9WFzu zy9GS=&|J4M*BzEScDy^J+HK3tb0fJAhS#1ser(>Oc{siP<#A^fjTzSwbFVLSLKLW= zulA4?@Yy$t)oacJN9WXc$8MNbLJL3M;Juvs;>S%dK~P#wbIt5Dr0lTw**B9()xlXdpaz81%>s_Q!*70=>R?(nY=Umlen#j6To2 z0%lVt7_ApUFG_2(Bm|VQ8ZFNq-flKkB&4D9{emcR$t}q&4XWz za0B7NXRr~GA&$F8;=#Eu;h#KsV!tNGfCLXNF9XAa%R7nV#j)VQuVOlQiPkH&DqgR6 zhvMgnc$Obf>{R?fy!HnC5(n;&<(~(jUI%ZQf%J`f1Dy{m3uJ-q<7a*$9ub1^39=gHf(avd-nXf;Qc) zmM;2%Z$268m3A3k`Y%4e9oP&yxYzu4V!4k@YY)LAkKO>~vDB}K*|Kg0+Na~su+%p& zNRmrd8!Yv1+L8@?2R5+O!$1p;%!4_c5LVyL5LUBqm z)3{px7lQHTTPPHwoF++LNN>Xa_b~5$#8XmUx9kaS?K{ecXXWz3sOxgi4u3B1k-W1n zzBuo)*L86}TPwImhvR@zto>TOhbO^zvza-X2s?R%GgC}gA~iDw%oaLiP8=;2JIZ|x z$b-L9M(V@6h9$u_e@J#5`Hdw`B~0+ul0XmUx`*X(EB{S3ux}np!mrSO^5Y58d>! zjWt9-lSb;^{P`JR5(dJbmkf|UzhQv3I3d8K!QIHoTt)w zbmysz20ilG$7+uJ%bMO(ik*sk72gfIV+>&b80egNWxKa#pWJzWbLJCc;|9#f&&T>5 zD4&n#9#*a~84<&t)%d-bv&I~C6m!)(FWt>`A1-)nIdgmGoq;m~(cO=N<%hU4~ZS3ka z%jp?th&lB;U4tdz%4@_rKGQk_9cE}I=<_mdjO9rdA;UZJB~#Nvj!EW(Kq43$PV}<{ zOO{_ASWZgB!*`2E-Lg(O+-BYEo||<~)eixrs{3$rF~Xn&vEexI$qX0qHQ4Nl7#_{1 z07bh=$vOBBJqADAsm(jE!%0at1C`sC(x(=rLdk<#X%3y9oJWjK!GCfgeRAkwg;(J3 z4(P!&K-A{N3h^dsJDl=O1_=$1JOT`znuC95xD^rdr_U8z$Z{V=qM_k;!|Fyzc_iOj zsUepy4|mvW_z&I6Aj!v_k6eBY#w>Sqgx8k1XVL{E(@GXu?pYrFP~;m3Vr=2a1$dzu zTWaJjM$?e#Y1sVhj=4Z+WLQH&aikTV2P+r-NQuy8;;@H7afCZAlFiuTy>zTdE_~bx zo-Gs^Nxyv07LFXpJe=*>QX{;pz&*#arAL0tkg}%%S8_*~o;%6&%Zj|kc%SfW*^%}1 zJJ+*Ct@P1|cms6UcBe%e=r^5CtkZm^Z~<-Z@MU&nJ3TLAF<^BfJj2&5^sG2`Jp2iS z+hNLhnw7o`_ID9xjqT3!YW8wEkubNwxmS2u2t|rX_sUzvFC1ZB+^bkY?(kJMDv)%; z_u-G1-ADzVhBMBFgy?kK_whH6HSAVJ{tafgnl4OZ4b!O6G(d(O%6*Asn3&5crj@iQ>*^y7t_RZ-+B^r5}=&hTkz zCH0{4J&(%t$S{WdffuhRvX~)X@NC7A=WW=2G-?pz&5e*Vbzj;dWteY;7SND1bRy#0 zj4+I)8%FCeT2bGhf;q(X)pmGD=u}vSR`v``s|kD!%o&U^Bvgq1p{xP>M~Ljv6_e6! zr$6f?kuG9=9u8ra(H}R^BYCy9Z^wi&%hCWhE9Cwta~dmmqFj!S?vi;q+}+8}zesUI znHlso)^H~Gp8D3*%xnfePvfR%ayW6%ch-W##lx4$``6tm>Kn_vnt`V}EUq&=Ka;iN zPFLSanfEjBh0Z34J2?|#FU!5y@rIy+%mxN7)WFj-3+Q`^`p(F_(1x|h`KH7z%G|@i zmucYQOt!IG?66JHfSEkw)NOHEB=G!9KACR2!za=mzTL@uir&{ceAwLKJDkkVlGt_5 zb_92azmb&LPLCU%b5TF;@OV;Yl>Rq4uZaJ?Bs2kQJ1IgkuFZR)PH0EiuOe8gn`~#w z&>FVs#_HzF#2qY6=nM~KPNX%(_G&*2VoYa9C>Q@HJcmCDw$t3x?aVir?cp}vNag{? z8==-gnViOSN7_5YH`NN$KrN$&V}(A8Y&pp%{vMw=JKb6*^T#aI z%DkDx=Q|AT3=e1WTg|=F;n3;~ACcL~xL0Xy&&a%lP4j8TmFg|d>}22t&drjO`I*CL zz1rcc%Nbsq$!lrcg=%fi{5a#5sC7x^NCqx-Qlx5^*_r%!am$>O#lFhU%wfLDC1rPb zhn>mGbleJw?hfBzXJWUZN9?EBr%IGO*+1e+Y%S*@R9ewv+fu`aQ~G;rV& z@~2v1c1bpY+Ozl^?MGKViO(vU>19;nN!+as+Dy|5b1So-lPv3GK0Wp@KHlgw^c)i6 z6Ld0ruWiPd7sdTK{D=48k9|w~gZZ7UBpM@~2Thuj31*LoeeG|o!#L*Wp4e@hjvCdTn0 z=u>iAa;sP4~>f0p}t%uBCmb6s0w%50Ti7e6El}mWnNm;!|8|ISg_Qnc} z2i#dDi(Bd>1Kvz!NsUl$C~s|PYg$~-GEWYJ~QE-9IP!L)g^=UjaG1toLgUf0B{7E3O?xaa~FUs5m$ z;0i~apIlW^)zX3>ybcHQKnoj>%Rxl9IV1y65~VJj+^Y;9u9! z+}=_prWy|T#Ak9N+ zK)brRsTDVCfAAF*CDlwAQBm^Brglx7H(X13XVe(Vk^!DAY`269S=Ylpk(o zv>#DQP}1tPW>aGci&7%B(#KpdTTa@nqN!<79m-mceUE6*`X+#vg_^R4hBE0!Fj=@) zv%M83Bw`C8GKO+UtFe2aKUHeW$aUcw(proRvCL>Pubgt*H#nULBfV^ zB`%Z4J@&W;$SP?*8@0-(*h@$rMX7l{C8pkFm_T3|2}w=zX~7_s$df+B{(5nQ!zGMQF9R%qW2l9s~KE2y|(OVABDBO%Pd0zpC zAiep4{H4Lqbe#PfDnd;+!AWJ5@e3WdtXQ&HH0L zj_74KEGT&45oMNl@DF)oz8Hi_#)MLk35p_qHWFc(2E3>BxI@nZV*a?N^plDlH)!W` zOuSl=;|%R975QaPJ7=DV8x_B$_`Kqe6<=5ER{V`(7)`5ah_tOVyof`#d{UMsQ8rP zR>hYUyA(rs88Y2`#c7H>GnnqH6uTAQQ{({5HFhDz5k!O^&i)K{z8AUrDvJm^ifLgv zj*lFB=nUw<(vY@=}JA>oE3#T_OA?rd#Jc0 z2k*^u?etr)cSQXQxHfKX*C5F9k`NZzPoYeYYvGb&+xBw(TL|*K{RQ5cSmIaQvFkVe z?J4=EobPaQzVlOES?EWhB2U^aK$oJFFR=RW)Sg1WkM&#(cv`Q;fYaDd(f81E(Nl4^ zJO7}faI_zR!AJl<3Ga|60o+StQiwx4B!Dw8(j}<`@au?WB!I3b0dzeH;N9?X44sX@G`_2;f#yO8_JgeE=Hv}6LP$AMCy@u z$Cm)kWjekDa0v6@O918lG|`Kf5qX$Yd4+r|}(M0?52LA6x?HR7KunOkV;> znUK?40+_}i2?-z>`2~(h0G*%&(E0!DeG7bDMYaBZoFu172%$}<53Ll)myf>5B># zLYrcnrjWKN6fES?ByDNhge2u9LgYgbN~GPZDyi zh}VkfMFsl*zO~ovy>ptjX?e)yGWnf+v*xk(%z`hb#efaR1+162Lap*ortm%e|J3 z8%O{@%9b!9zew&!8EeRBZa$wNLuTfVV&XsoIGSw}NC2CeIFJC=GjV?jpwnLh=mZkL zG$!sZ0dxWhAQcasKmvFJuNz1JsjB7#62LxIA&>z6l(B&XkWZ!)k^t^ubdm(n2}uAS zB)239ptG+8u$pW_5oFE?@-z-2NFQ&{9}OkC4m3I zeElVW&H*HVPq7jz0dyb%Jeu4B31BX{1rosTlk6`6bOH$=_lY`z1h9&?CXfIQVcz}{ zKqrs@%11#U0i4LZ{Uv}-k_6BRNdPY-?~nxW0+u8s0sJWALlVH(_%aDe059X^LK46y znEyZ$K&oC0Rs!fi0+_`D1QNhaBo9>rh^~4ppH=-WgDgGLyLrt^h?@Kf~SoiG5QMMGoLK7zsJMf1M9K0YU2 zGT*&)oo-3kXD@Ib?u&7}N99>L*W1@Po7|0&4JmW7hI2;idC@M&+hPdgx*gu_zPxCR(s=ueyI0Ww)6yl&4fmIhasrcw`Iv5VD8P7UCZ(H# zcN^b)#+h`E4b!8K^sgKLhgFJ6}W*vkDF1T&`^CWtrQWVzw)kX(*@48QtN9(b(> zAxC4~j5F<=EH~T}lFL!rWB4V@4QW$%lwk%mI}d2phL}SMZu42v*dRmxnU9beZ^D~X~~bj+Z6%geht~sbVC5mb?<>6eX7+&4Z~XP zVC06SNH-X{;Zn*2aWTpbkHn8fWxZHe)0g43u0wyBOpiKKK0_M}hrb(gLo{#5g|E)? zv*!fv?T{0)ZRj{wP)^A4!8pc^HUyA#VG`YMvJA;#XiUcxO?0?CjVs6xS2wPfjY-47 zBM;2jKPW@2BI5owYq-b^AeUjkb|A0db$!_a#slW(J|&qO1IH^pMe!8HnTjGyL^_cr z0#|7G21Su2B3xvNz`HeEWQm|3Qd(q*phcDl?9=c+Dn@a;SgxZLMV1I!WQjnLB?3j3 z2)t6$i7XMc$P$5fYq+dpf)-gKP-KZfktG5}mIxGCB2Z+BK#?T^MV1H@St3wmi9lJY z1&S;YD6&ML$P$4fO9YB65h$`mpvV$|Bk&-xp0ZX86j>rrWQo99jTc!WXptoXMV1JZ zWdoqd5`iL11d1#XD6&ML$P$4fO9YB65h$`mpvV$|B1;5{EDdv>b>*) zeSfqs?VXXnsF(6X3{&ZL_US$7`Y)L?(@CQW>9%!I@88AtTz%qgJU@LP8QDQjPyaq! z5iUe_7Hr0YoV>y@ZCx`&;k&18E4Y& zquc$K7hb$d_E(tpb)FA>UBOp49=t7z10I$xMe&(Z3-{euN_`(#hr@OfbS3sv&dcaH z&kFZ2Z)o(sTbRzwvzwVXmXP!^27VU=f`hBlLubw z3Ovj)deoWrI}dc2Uj);A&V9*`;Y>&V{1y!JyA}Kf`i5GCbYXru;AhH*&tRY5`Xqik zlgekVm27_^FH}B$x0v$Xr2OJQy63@>OJ3CnMu z3Xj>rbx;3wHkvsG@SD-Rjy&B_^S)7?ufzC`W53}%@FYb(-=t?NRw^!12^ zHsIno19Dea_g>5G5!=RPu(^i0rIo;$jj(%6+sIj<_r~0wtT^mWK%WhBF4%JWGvXKZ z{sZ$*JN|l~$^U20N5PM_oQ>@^EBk4rT_AJSa9W%XIpQzSWwWxM?8CY1FCEZ!MPRNX zVvI(lc*Y=7i|v7?tZDuAxROpC8kN>uo8lE)dH=H3m!#MS;A6O8fAw(o;GG>s=U zdj9GtCVeML@B+(PhiJ!Jb$DBarF`C`1Ii53pH&vv@xnOFpLNYyy(wPeUeZ*F!+toy z#3Qg@J7}!VzV?T|i%^dCdLj4*o@bhy!T4Z)xt|%~Tb*xemEV<#<)IiJX1P9?S{}w; zr-9~`;U47;h5ODcA7OV3IrF|vo>%TKT_N(CJY>UktLaf^;`rV%enypo;FDpTi8~nq z;dxd$h|eI5^R^WHSPxDylIN8#g8|=7#ui9cW(Jfp^+C!wksl(((Nm z=Jz6qY=pgC^U7C4<$F8km7GVL@*Qkm`6erSUfw3puuHDp=YBR2? z7jwc9I4u;sg1rw`{L_8hN0Aa=pRnTB^x^!wmp*oC7tSSLkG0XN?l|^gM9R8)vDoD385 zRwG=esvC72VIyB8wh>`vJs&+S(d(8iOxR^z!?JL_zP)=NZu5PTRrRPoGfpgHe_1Uo(eiyZdO50>w+hN=7355Mp!tkAl zb=}S$<|EqiDnE{Ey=beK#(+2H78Z`Z517fXA4kUlfM zy)S~W2(FWj_H5Pi+Huaw7xiKCK=YJy4wxHv`vTnoJ;nhCjpUUkWs3c^&?^-m^K1or-q?A5eb|u3d?8Ey0h^qg_^x zzUTGHG3XbQ*sruduD~;ge%Z4mcSoPieM)#%@oaTt{;_j-)uz5$#HYvjtZna$W^d^s zzi4a=_}#Rd-;GQ=i~ZKx)R!6C(pRngGQsbUZwCA@@7b9RewBmZhld5rsLug7OT9qs!CoQivLo^qMr<+U2a9Y4A*<`l=96+9ey^5yjzer$GWBO;w+Anojq5ey`CG5`on)y z_QQ!?B^dMZo|tu1ed6k)>JnCVUBbos1NPnh(z3c9-dNh07#>5LA`RXj2{&GsNQ*D( znP^!Ryyw!F&~EplTnB5T8&LmnSH>U^X$-cUWWxB2Op~P+R#}jsJUE+tQ zb#>1yU6!zRT_@#S4}ayT`h=amEU^yAvRN_EQnp2j=lAYC&(2;aJeZd}n4kHWhka*w z_NKlk2GMQ}DC@&EuB;)@cv z_p?#OH}aC$`BZn&1K40*p6D%Y>}?v?Q~8}Xc6b>N42ljrczXX3Z5VD3#kA326!8Da{7 zH)|3M_2MvuOFn+%aIVOeD@yTV_!Zzc4!=VDIA8VVZ{E6i8R$y<)#>T8dSQ&)FmU zGnWHw0#2}3yFUxELo-k+@*H+NJcetIIWakR3fOa7^U>i8ko_^iK2zM3fba~@9&1eT zjxeTpj|-BJUtU0-mMN!JOo-rPLYm)O@4NyN8aj3w`=|J4);qrsKUO#~Ky%siLI9RM zHSl5dI;?jd1wazo3-#AKe=Skr_0E$So#xIVhcx$6fSsC}jX>B}DsV=p{SV{wMqGWh#@pwlb9FUb7|p$e@_0b(j9tdv@Ef}1rAMAiwM!g37C9S^(+u2g7Z?RI@pJCXySma~2;-CG1coPi$4!zdV zywY~kUobiE3Ub~h$t9=kaJ7zZK=QMh{1%h^Q$g}i1<5~6uQhxF{<&k>^tOHM)yPZl zh!5jmnspr2?(A_FAvDG9K+v&wBqiW_pFQ~|Nx;r>mDyoQ+>lv_o9Fo~72u0bXCX>;cjwMgp+aqG& zdc)ncXwIqN+*_1Tsf&LdZYWiU9sN1?aB)GcXrb} zcgL0MR?Mv|aUZdrf7o`s^b_s_S2is#0;gET`bNMuyV!mD$_fc8@`CG?m2 z=zqXpRI;L^(!BxH>l*V4QvJWHD%`(XO{MPjR|$vZ^H;hLTNU$)N=n=_E8NGeCiirY z&y)%`tHPZbogo)3Dt2d8py(jn?_F6Oy>!J6_s4dHdt!xqHq*OzU0J-L5K^1MctfR! zCG(ywa$iQ>Q0V3EH?Q*8K57-a=SYsvBggU*o_%v07=5^@S&Mv3vHN|gp8FUI|In4Z zX8caKL=Y!}7P~)`=xI)#$b_2lnAc&^yA{8Rx;54rjrG?2x;E?d);8c}R>j&i_%&On zuU&2}YHYR6TGnBm*>bT}4&#DIzqSr0^^m7cJm`+dyKDsz`Bydrksm#Q*vw@!@>vUG zi{#bOxVpZPYii)xLLc~=AoBP9i%^F7FmG4Aw$VDLvB9cdvDP}HZKbscCjcX^0mWpz zAkwtcgLG>=ALdCb*0xC?u7!M(4tdwMi4V5X=AJxe{>ft&#m2N+W9E2%mR0=3b59<# z`s6VU61KWzOhd^SsS6+Ju)Y@OZpQH~f<1hdS;K>Ed>paq7{@`YDl7APd!w^~&#}qN z_d*9o)$*+DIa#sm?M==`cLSdGLZrc?1IJZozG}D*Do-hmcGEy$V_lnPWs#}K&r6S3 zb!G}dvH-tw{2K6Md5jwe&U_8uk6$>wc_nbf=eme-xUiU)JalPX)7s|dnoD5E5O<+{ zf5s;z(dw2p9V)vsikc-`i8HAR##tN<1s;T;W5n> z9D78ThbNchE#NLhxfjrU*jL-EX=Ph`2W)*_W-)Cm)Ekk!UFD?x^wLl|&thnMV}~?N z&6>4*Mrzve1k|m@GYYGdFfQ3DH=DJfMN>d9FRO>es8?z6lyI`9wmQ5kLws7-)~`Vj zt`zXDgDubH4Dv4Cp90UfZZXmtaPL?688#yU<*oWPA&+rnq_-Z*@d=N;FQ*)@w9GT$ zzYm%22O4MijX^+|-y``LBfyCKn3w4|WRxfxehv8W2|He!ZVGPLRO zj^S6Yc?^rPp<)iMNX}_<*Z}PxvO$Wa@^r$m&KLAa?DSIc<@thCi7{}jZiYEMvSSX%uh)^6B z9ocVNO%xZD2O|&Oi*&svQ-~k09V(9XDjvXA1RRU-7=ElT>(4eZ&j!3!11{P{k2>-h z$c4iXu4fp&0tP|eKE{? z?p@q;S81(mCivt^x(aa-|YJg?;~sxXpDWrIL6FxPVVXM_1Ls0R-~uJt zZ;nFyuL`22AGIaXSAsV4O3Kr(Ns7n%yzkf#*|x@!_Fv*u>}>ERBEgg@G)3z-t>{0$ z>o+MVsIx+oqVV(uZ$hro0M;yn!SH1`Ul@LGo?BPn(uRp2EyXv}$OFG3WzJWCBIb?q zp5?sJmc@HVIsA)?yA`Qt!|*+duPVN#DEsFS&xe)iB8pMPOvP+P9;?E1!xi%sV~Pce z;}i=O2_8XDF5_mMhLttW>O0tX5p2xKy!Lu|bh1PVqX;iX3}LQ%{I^sp1ui zor)V2H!EJJxK;6H#m^|-s@SD?r{dj;_bT3}c)#KUiVrI8QhY@5QN_mJj~rzDBi00xFR;aN&I!h ze8@DEj^UNYd`lH~E8dJ(8^a%0d|j~tlU0U4q8Ps1l+JbU|64vqh~;s#B3hthW`azUHv zA69%?@wXb^t0?b7q!R;~z)=_zc)dA9{3;d2s380`N?)Pasdxhs`EFPG-xQO_{U;fY z>u`S%Q7rudWt;|z-AG^to*l;LD2`N|pm?(4EX6Yw&sA(xT&H+F5x>tX{Z*x9+`)C8 zP`Xd!|EzRs#4E>8BEn>UI?@-Aj(PG_wo?x8D<=8FiJ+$_JzZ&e&JbU%wDe>AGnlR6 znDSszwausp9`qbj>}QS-cAv@JC^MX|AINh|shek2E$E?cpamU06nzASs+ZvJrtU~e zcA&RH9k+<(**!M8b)qMTw68(lez>yZLGKX>Spwc?j|d-Nx>5Mq_v4?DnevVVdqgfJ z=GFnYN93POFu~s=!s~O7h-G^+rm8duBt;Tx zFOF_xDM%iH*ZbVIw|x#>d9ZYH4U()Uvj1 zS!3H}F`OO~n-q^vy=Zb#>3HjO3^G<-V_iME5!Py~6hxm?T|R%VmFnOFueklP)g5*9 zKzt(!t}s3>M7}TfAY-Z0!fVQ3rQnzv?p@w+=IvF+nB}`UAUK}p;K%$-7sU_LI-xO) zi|bT0z_fG|@niafrQ@8!X?laV-c4^UDSkQ@%{ZneuTB!{i~4Ht>tlqt3*w2OZ`&JXF3c z#2J3Kz|TCqe{?s2pKF+arkB6l6#?Qtg$}{@xp5}TJqSQls|o)Zh=JMn%>}XJNTO7zUShY zauDAM`&PWybQ~+V28N|t-?81yD~wBAvd4r&HUkN81^CfcRhgp9(NerM&G*vYlP{xu z4jz;XiRdtVt_Qi7xxKN~pJE<-jyZM8l)=t1TfOelZ;lyB6!pjCV=d+Xw@2XzU7hk;c6oM${?o;5d--zDaxur?@H~iy$S{MLQV&TqUYV1ZVrB? z>6O-MmkFS(ZvQI<=Z7yer^H=^>W3p8Mns4h{@VM>=_~ zxCCKgXWpU7bH)9oizBniLq1GbOOHAeHxsn+Gm7IDpDN=_T=HCTIO=gX5*=)=*Z~HY z7-m4jFL|!GE7UG*57RF7p*&@U3{N1hy5XXZJrW*oivLw$H zZ$Y}j%oVwQKA5>;0_)m?nJaRi9KWxOGvzb1!EpGyF;`^Y2weE;@625B2)$SVkaNZG zJC6M|ct?6uMZVTxxY|3y8nZ>F=Unk$JxAP#a&RuVm54riv*PD8oMQvyS86!RJ=nQo zYhYgS;B&=k)20u0u2_aSHJm?J>}U6ed)Tn9i2mT)yRk8c#ABMicbBnuvlM%HxrgtS zKJLMzy&Gs!Bz)U8@h2r5>y}8fNBl1%&0n!r$-TMUr+m0;mf>UE(gt3@9KbGPl-pzG z%B~XY-+2eT29R#vDt@f=18sgPni**${FsrR1Hg>*N2JmWbb-Q*^d&D4|1l$VQBeej z?J_>ah&1;r0L(@Cj5*Ixvm(uY9sjY4N%0093qiY#uyixFmj0?JE!}wj1w*j9IZ=YE zhhWlJC|;~a{01o<>+AggwoXv@vlMrj=X2j z-nL^Afa$?O(-k6($wMCGcR4-kOdRK`#?Pn|K=A1>&cvOJfbg8`RuEkXljUz9>%nrJ ziJ!>>ul0Kn-Snt4aTTD${AOU_V}A0(G|A`3xvI$nuWY3nM~^zwe&>M>^D76xY=n^? z!%g`(R}J%{?#V!N+EtnlAg(%8z6^vIe(T{6^E(PRFeW*?e z_?ah;A05wt9BBP>JK_v)rker=1I=mgLL6B5U9K7F1~aF<7U{l4#_G(qh~Asgi=ZuO z7KC$_1Fe5@^$oX%~$!%aNgOh$nQPU94CmI6mKM=sXwLo1I0s`r*;Hl z^MlV*Pl^wEo=O`XaQ)_~#tt~zafGGwOpI2;WGz$Ynz)ncu(#No8GG_~u$146I6E`` z1P%mT2sc;vPRG9F@C5beyais(rO(!i zAL{u)2{02>-djviIgDb0`Z%dH17Bd`sLM4@1fBuP-#en zG?kNId!oN`T1)c$=&uOln)tse+7l29hH&`i5=%Oyr&BpD+Dq~8dHntt%u~^i_dida zXjjBb=DE4W?nONiIvZCoup!Q7PJ1J~lmxhdA)=muJ+t(6$> z-&>xFO0D*p;c5x~l~YvgrCYY5u@S4pEvvAbOlRSPQ5E=Lvf2SjLjBqfY<4;XH_&{w zHk6<96uMFPaiB48+?yE~axw^kPM)vsFI^$hnmlB~bnEF+XX29QtH&UMPmFOU-N^_D zzrF7Su?u0G5Av14dT{D8(0uhj5pJB}SAl>qKkkKMe)2oee3d%WF{Clhv>)HSVSdfv z$Jqk;G2E0ddA>>=>cPxcuLzazK=aiU+`t&FXPl{T@_cnesC);SuZ{t~9K!@m>E$nZ zzREMe2Qy#22c==aUg16>LmOiN;0`j*CEmkc(U3PJzot2gvmnV;QV z34b}_tgW;~NFC9x?($mNFZ++aBCNr>uuC`}`lB&)TIwbr=qIyFv{4ujl>SiS$aApi zX~T(WBly{!UA>i^sAFP#pB;a*kL$|ii7kD@G0$HL`(x!6+6Hm7zg})_&n?WR|N30o zP)Du@(-)m^6aCxW0Wl^4Gx*B-%`VM zh)*euZ%KIuYu+pa%UWKG`!zs$e@Po!mSU2j&#NFSG8{G`v$+WYP))8gKk8-%|U%uLH+PPPA>xeYhXM+vxm0$&<-2pm?-o582U%BpWCs&NqaI3@`m(Mq>Uo2 zmFQYOC$VK?0n^83q7AqA&56O@XD6@qS|7{LI^z8+ZIy+#ngf2UXKLvtX&0tLdwFev z^mEW2ub`gta0dT6>1A0?JgKYqmKp1NQe$2px785Zi1-I$nz`%FEYsu;p{u^%t&O^bCw9p;?gw9c-tWp{Rc9y@7z?bR$BKZt4SVgh(?r8)-Gp22ZOCzeHCr`$#OYrH_4T4$4crt}(QCvtvDfH0&(?JMhTF z@*dXb==k+LHteioZD?n~=_C8tuGgclu`C(57VN-vr`E1X@cNrh-;glZcdeUYHW79; z^}12CHLsNxUy*RjIup>=@5R0cTqmce9QXgovZs4Lf&MZ7$m(8vPCg&QwMSE*DgKGR z8+3f+IPkYO_ok0S+z_o7u>dr$MSot%PaY>_dCq@1mt-&c0&(slL{}~Lkst?9pU9O@9Q2X zb*`p;)86MgcRYh<>G>x*cl1ST`98|&wW~JvJW|GfWu=?@?^?I?yVuw$+#7{mV?1Av zZ7kiKnEI`G(sxbza2=e}f7)A6(bYY2GVBuq)2Gp{adr2VxDV;mX7vnF+WA^yz5T6u z>#VNC`l;Xg@B11X@ivIs;%A9W6IM2yOq2bGp#LNpTHbv9P%)XX*rf+`KY%6`RDA~ z{>?nZ)mjLv#duiT{B^6W>l=GvbJ~hsok-o)bvu4viJJtCua7@vx%_~cC))8 zWp&sBc|rHA3$FgUTeb1))2eFkNJSf_lx;|))NV?oq8~*PU0+L0blskYbZJ12-D$0B zc45rg6)9V@i(@dphn~+wyK?L>(qeKp#Ljo~x~LPiAR7Lqq+zTJM=jZ~E#qq<XmrzCnEU@FE2uJv*(DBHXD zv(xZwPC-AiCA_K|@ zseNCaYFPIL({q?;g{{H&jm&_dS{5SSqD|2N$e|%S4cwch9 zJPiEn2H zKN)90;b*7L>WS@4bZ6kl{`6~J2YEOyVX-%TZvpyeJR`w-cjSpPdmPN2)=68B=zZdi zz3J64rd`kg+XzPmJyWZHl zlk@FI9~#+r{SPY>hF7L_t33CgLs{dr1zgt9!+BR4%Jr(Gy>6ehX5PkmSw5bh@|k!? zW6auscRJtioF7bCKT_rgGuDrMOY;NHZ?a)8-L=;Fb4s>EJhBs8aFzht596q7-6rkuhyC!g?!$LpSC{)c@PaMpm%@GJdh~Jj zq3eK`Bd&1D$iB{`x!Fv#uW55$+q@I$H|=>A(%9DJz2GsTU%&d+`X0{DKZ1Db^be2Y z%!I@?eEahohi8$Y{Moqg(CG(18N;~0lxI7H({UewNh{Z_3iTcE;d6uj*PVj?$Ni_~ z+kyK9!uw9~{Uz5ppbl49AQmaWrhtr$Q!}tWtKlhg@dcB53=9AdV7nO0uf~|~5M#_Y zotBPG+KeOoT?W(Bu{g!k&!8VF#*lF#&lqwR5~*B`iHw5P`%d*+ygjp1E9FDN6{ ze)qSj;sk7}2uTP>_7@n&0=vev1TZRH+vX@6N6Ld>&J+^)8Y4tO=wd0X_fhsxyA1iq z=*3W9vO7_^D>*hGJ+sSVX4p!>JPEH=I4S_jq2=$+t58NosVpP!Y@JE_XMdGH~}5%&Ocm_{70667H2C zoM9AmMvlc5BWXvRh9W(Q-*9Jq#`%n$z!0#^s##?@6FvTs3^`_9sc6Q-Orlg~Ms0}7 z&Z@c8atdb)j~wgBNCMZ2E(ZB?c%(+?&k4|f#;?FB&A>;6NwKM?gY(4@3-2{Vy|V-W)KTG$eJb%YUKut%&op+_8vsPIAZ<-6i*48#u%Hm{d2C%d9>b z&HYI@HZ%7tyoIM~-t63b@;J@88Oz8{eopQ-<~?0wXQspII{8y_UtvR*YwYyg zr^)IJ=RV0hBX=2N=Q`dHRF>Pw>sDyu^4yQ}(VeHUb8^#}x6=8R_^MRP058ExnOkqPr{hLMdJO({?^ zHTN(|$SW;%u9lK4$-SMi=R16L zIr%GcTgd7HC0ld9&)^y*FU|cGuUqTj)E+c-r=5EadDS^sRRq1+&fUcLdbzTj-(~0C z#*!?P>~8*@cCJXw8kD}*&fUWJMy2n!b6a@1CZ!*=bFU=5T$i=OQQfQ5LwxAFt^;kZuxJ|!ABOYtB$u4quO)GjQ{`(~vZjIp6V-MrwjZVYJBO`o*MzHtV zW{ml{gg=P?(HHTLearNVi;)=W{SjOb|Bpk}LG^;0o$5Y-xU^GPzT+l9yxfO^M^nr5 z8m!dWWRthjNKN7~3~|9oD1&+7 z7mU2nco&acW!bG#$|WP$G#LLm@S|=L;okU!HF6Eko$!(^9=YanB(s;s#-R;O zp2txT%f5(jZ}-MWt&!L}(dM<(BOs7Tsa>E@V4>^F`zOv7?6XHk_%gxZjEWXiu}4=i zWv?S$n%%ity7U<7#ohACvloqFC+>FS30P2w>z9~ zx8^%h&IWneky}^2ErPpejg?9ENLL;mJ9(?NE}n6j*{TegBmzo`UCRSQwMY-?;>)8QRgQA3-f?Vi86W!Wmv z=kFat`>t+nYg~q4G_s!<_X+a~+)F(5f3$F1!;uw*^XA6gi;A(mXui{wJKw$9zRi8o zt(b=sAS!XF#dki|wDEfzO6C?$b?=W9yN`dYm{@|?dG6;w*5r;`x1gk?u%zU{#*(?E zb4w<;m+dV1uA9+RRN{^)E}2^rubeYEes#%e_vi}u!s60raYgqc=!h1TI)%d~oiS;B zQG8*Qn_lc*TtbCbzx;? zrJGaXwiH*A;Yr2r*PP<%w>n~NtSL2(g16DGS^*to^^#s4^J ziliVB9bYkTQejDHV$YWm*%d8?mwJDPaJNZ~x3 zVsf`r;cmNXo%^Mazv6!Ts-_hs^9tQ(aFWPN_Ri>NlmLg%Y^ZdXR;+L*?wr5s)j5@O zD&0+2g8ZTN3fk>2l_lCj&s5HxH+Ok_N71}_C|7z#iMzJgeG{CvT=^gFP0mhtK{R&e zC)_9OijukW<`pd{nY(<)yrRnG4W8Ksv3VTct2U0kQ5}c9@~ndLtgZI-_9vZd-A_a| zr(Bb|DQ#nP11AJIp>)g#8xPl`e~Vo^MsfISkZ?WEqB<*k5@_l1))0(9XMl~jLAC+k zro%B9c@LPyAy~XkWHg|{v01KRd=2-&VXv(`Wi=g*Yuc@vrZshK%YiVd-!NsmRnxe- zv2A(dnq`++jXWgcFzh?j)M5YE%8s~IbICFc1FP#%bOe!X7rI>o0uAMUn!)%$ZSo4b zx*q8-Sr+6EbF0xq6ap?ZM-e{Q0`I~mj6N2=n{mXIceqClv&oSg*m8`(mNhjjY>n3l z%b>5kvQe0DOY-t&9GRl+E0^IFYF4gpUEAiFPOfQcu4CijT#03u)}e+CY{|y)9M=6jMJ`7uK5%g<%n=Q{eu9WzAY#dGaJ| zU&JlMO+lK5#m97+{eRhmZAdT-=2f^(ahp3*O2s z6Aea7<@%>DwqWuxfDsPug>v1x<{GJ;S9@F;*FixVFR^Mk)LS*H(I}x_ zTysg=N_Gm~4>?o=$4S(!nq;l0ySTBYVdZkfXg6KC<}&H34Q(y0H4QD7p!#bXFYRcu zS}ty)C2KI?$sF44w7F~6qSOeiX>VAGc3jzEU4n)}12#3c;B1o`c8ungI7)}Nf!&jB zTY1*3c{K}X&sw~2(cH6V*HrUf)-_o5_4O+|+O0ay3vpf!E(*IU9d$v&irG7q8KuM9 zuO`%7a&x4LJoW>nkv6J1V{YYaQXcZrlPa+=p&o$ODwtscU9)U$TN~b@Y{@!yrH1yF znia@JKW=3sP7=Z%`qg!pY6nDGG<72m`$2TeT2eLWis;P@#m!)m8a>iYg#O+RZV^KDjhZ0&g_wTU+Pybm)o+YZVjIT@22PtUR5I&0?~QpjgNcNz@jX! zY=MoOn&pih=va+4(t}OamLX($3ywo#Gne9-U9+~m5yKs7iR|smc=(Ig!$KXdA(@$}G@IDSZ#?ej0kLeGVE{-%N54;xnWq2=hl78IGke}iI zghngh_Qn|=ry?L+j~FV<{d1gGunkN-7Q%1xz-vWfmh}%)*ihV=@Q3-W_^?+m@?-rB zzxq%fc*Vw4t9ZPBOhe-zag5{dc107u zaKgZQBJM!~G{4_u*CO2sCR2z94O4_}lXW-J?HnK-uN~?}*3(#sp^atep;3WE>cM)k zu3>(6VlsIVGph@EO^{$X{NQ^c!dKw+*ouK0j19x|AUS-t?6Ix%!=RW4zbA5JpchYD z`JVIQX%pYjd~`aoRBLGF|4fKx{8o_)F@=|ZPl#rG6&p~T7tgu5p&5UFh&D0Acf#hP ze<|>%pW6wIBR$>_K+@Ba=vhfL#|Pt>zQzzh(rc3Fk0;UBCDH#ciN0HDeh&>r`+hws zo@?L6@p=y%0!aF?B>ERg^ly{s7nA6hljy%B(XS=ZZzR!ap$CBFIVy=hHi;gUM03r} zIOZ=&qRT+DZN?#v?emeO_(e%{4QTUi$n;G~@y$teunDjjN+|| zU5a-q@>ylRdlm0fykGGF#RnC4DL$h3sN&;_PbfaE_?+SkiV4LR6?ZGXthh(qgmV;-ZI1@0}WB6!DJfmK|Y+^p<8bp+1;lZ%B}l~k zKZ=(j;|qwO%M@uJB@dE~R^k*KTTO)K>#f8X=G8=$GY{`cmU|o#^b{h>eK*j-i|1Y< z+Tk^ZqrR^bQQz!LASUcwsNxw@&BOwfQ|Sm^stkv+l<;jJg72L~l=D6!%9V$S8m}KG zf?qiilc`SP@wmPsjhV1~am7j^(sdBQce94ys^NQx;QPA9S7t+0H^!R>IMb02G|#T- zB%XqHA$|zgBhK*V15AhRaqzvA$ACA}0mmz%YC?mx;7r9c70*{(p}0ZulZu~Jyj$_x ziVrFNLh*UUKE*#OM)_Q!JVz@QD4wTSuh^=1rQ&sppH;kD@mq=zA`9QA6#EqaTT#vm z!*%&x#OsexJXP@{ii;IRD+1|6hXMF`4gZqjcNBlFD9Q~;m&tL0{1vAumMbn)tW{j2 zC}%k#-~CEIq$o;Y2!B~=eqWJ4zblEyD4weLgrZ3F5id#=!2i+kRE#&wm#bKyI9YL~ z;sQmETTI`gc%|YN#ak7>q_|V@CyLK0?pAzFF^UdMzQYwu6i-v+c*yv36q^(`Dc+!X zo8p%hf2Q~w#g`TTshG}j6MTAVg%zS zuRBF?mLjE`4BxECjiIDpR{V?NKNM|@uZ*Y7TjD8-a@G~-b4a64epK-n8vi%NqcFZQ z-&Kk~S4=3*#yHCOPbq$0@ry+8{ifnh4d12okCgtY(oZP;tkN$i-K+F&rT?V#t4jY< zX$nACjx-{!dxFvvlrC00Tf^&>Zc^N=;kPR8RD4eHZ;DrOd;tF(j!*bEoCvy1>2jr) zD7{o^bSDd<8gCp1VtAH(GN)TL_&whz#RqT0sA?!Kf>$udaL($v=bJj*dDP90ZR?{> z_EPMbf*yJ=b+W^@*_q=59qYr;u@0#=XM|Oo^ZM&i9}hBQkm-bE)n+T74!Fnq*n82=JvIkMSF1aD6bwF6B*}gH4kGM ze68jqSq5LL`L9fQl9w+hqZ&;gXf^+d`FyQrQG)BQ)w~{=MXQ;g%7IohjhKYAnt#s7 zK&$z~4K zXf;2}O9fiZpF^5M&}!aHmcCZ=L*%s|t!8`~gtVIPWx;%{=2@hCt>$e^>1#FHyn?UQ zypt&pL#tV|qJ6FAK3-uzTFpG_GSF&nWlCSG`BB!-*J{3rRG`%??~{XTHUAJbcyDPn zx1q+O)jXGt8)!9ulr0fxHOoBPkkQ;8CN^Yd?kFY>w3?q{y9HX!6Zv2STFv!L9B4Ho zEgmt|hMk^!4ig7j&1p;=Xf-!6aiG;qRV*jaYM#w|5@#8RiSLny)9<186nh$V!B?nrUXh3ACDj#O#4qb0?VxTFs|2 zHqdJRG4lpm&0JUsX*GX`i36?XVI%{sX8B4Aw3;U}Z=lsYhKXwft>#NfhqRhwELTXY z`FOTTNUJ%6@gc3|yO}?v)hxQ-A+2UPfATKVShI*;DU?yAGkR`k^p6P6vH6JPE`p-Q@st{A^%jAQFV|Ese989!rj zpQy3weXU4ZtYMk?sma^#0J)H2i?3JFPkBVNn|!qhRmbr)t3;8+S9SB2R(GlLQ!LkwEjzs3ob=#=dGfo<>rQ z>e2L96Z6hI>aS!XS{Kw^5!IYPJu1)w^3<%1R*Y!F$UjlwF*DY)o~8}RHZ=E#c0N`4DF4c5}2p*=4*I)sy(9IL@gXB`$z|= z_tb!-$bMS3KtBRq&%3q#bms<85A!wNRIN@m@I2KsuLwzcYyFgdjE)=oimKMJN}?>L z>On@Si)uBf0%AqC28uUfO}c$ljEuhCz7-8?Qw4fqs%3Y$T0!O*(~cGnGQOHtf9;@T zzR5igx}9R-c%Cl$dA9<6#?SPpr8a1 zm+#DV??DLq3cs$!U5a!k7y@Y0CF?icf`FX^q+@!_hJCjSicN6@0;IiJRJJ?oYWgU= z)}EuWmyaHG=DLP97!Lne={E)aD_k1Zhdn89uFN9CH1A~KTxp3g%~lSa3k?&t64|^R{GnD4=IWa6!9Vh z1_fg%G1iVPGeGEkt%K!M*pulBlLzYiupuo>7EizEhA_E1A3=}9bP@u>_ zfg%G1iVPGeGEkt%K!LS*sK{Srpg@s<0!0Q2l&^cB$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1&Rz5C^Ar>$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1&Rz5C^Ar>$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1&Rz5C^Ar>$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1-=yZ`o&*~=yQKp?$>j0^Z@(2NV-_)_DaFj87NIX-<_JU;0|vDj4p)BmOM__S$LVzJ4k{7-jqd(54@ z7^=vzg(H+_kGZOM4{Rq{Ww7yB3%g3KUEO2R{E*Le+f~1Oaj%uq1I>?KJMr|3?k>;P zr5!6r*ah7fJJW6Jg%=qQ8%ZN*PZ0K&Y%8%T1tW91T~*%0d^Yl-8D7$Kl81Iyy*#ju zR)#$Bu3>h(Jl87Q)R$s)b?+(j>=dJC^}=Q_h5^`XLY&=NF6Zf4WjA5SqTRqQ8}@}q z(0(n-Oqt?gpS8C?FcEaBvJ!(zm9)AsIM8AA|X2%o#uWF zfD=%L;Hoy?5cY}w(I=0SJUrvcj6JG}{})8rkL^*C@$4vHBKD|J{D01n zhX`XO&(d^kb{!|-7o@lQ{;G6*^jY?JNwZk2>)0p7kPT)A7?x@Cy^rNN5-bPX!z;`A zlBdcm%lYB5oG)cr=*`p|nV-JBt8@D5$D=7bc>QIriMD%radGpSxh2czx<@p{i;5;q zEx~R|_tJ{xCD__I8Cx-{3QMq8c3zP?t)dvCfxYi~7?-w;W0mJ*!H<7{#_MmAvj z;Lm&*hoQ!rfS=c8=HupF1qv$==Csfw5#$qy8gLJ!>12!#7PtItvS)}JmfE?o3kPP) zx`4lM<9iN$QN~|4k=2fXtslP6;>rNaDBem*Q04a4x;3o>Y}spHwX(Gad%^JWj-6<4 z%gNv9ywV9i_>@1qPkA{H7(Gk@zLHHnFnCG5 z0OC?np?@<3(D36o0KfN)%gYSfh3#SZ)x&S{FzvnIS4NLI)6U7?@&(`*LmHNkBeCI^ z{4LM9ZZYDGGuKM~mM;&L4_=>N^0)j5TqtIk0S&+P@Q3SL2_~E`v3!_k`23Q;B^H9B7I_^u}y2at|ssm@~9#wJm=DexV@o zn(L5wH@@Y0F9R38`aAROexzRPaUj3lG2xWy0sA~@|DA_7G#*PNazx5^IdDAF0Xa4hPg3NwO?tLsrQ#yRTE#ZS%M>>%ZY82q+^l$)hJQnG zm*US9-&7RYJMwWq4zHg_1RYbHq~SF9#Q0Urhj>0S;q$nsD9!%Qcxe~>Gsx$R;nIT5 z&A!s$!M;mQicg({j}o|pf0tY_6c<64Wm(L3NiDuf3XMHi*lo3I@qL1C5@XYq_F7Y8 zo*iK75Z3zN3wYj+va?f%;{TYeVHqw)2nY69qnJU!Dpoabe z=Y7k}5WngWicW^=5WdDFo(|!c&`F^~SPOs-;hA`?>_>-?J>;-;2*1G0$B)8)m|x2{ zkJRXDp`b&^y)y&p5Xw4^XBqW6BvBngTI!?@p%|?k*fQ#Ok;Lc;Ml>G{p%2JAlHNF5RL^u(IJ$U^>qlxlJPlS@gknV(YK&OSdW`d9m0R2 zp9VUFx9}RtmQj;*2yaE2L(n1o1X&JBhwxwP7HmH{gk4PO>kvA;g0Dkp zvxN^shj1&o_&S8I@Cy6UAv~5Tf8v$1GUMY+xgQ>vL#DNYW78CJ^`8tHR zF>#x9l{%!IM5;FlX)N=!W+r# z06K(Y7{9L$q0C=GmQlZoWTHbT13;idh(*J&4&ii?2hbthjaCvJLKwjh>kx`qBG4iH z91C>-9YPvx4eJo*kz1fc_!P4TI)v+3s6dBs7Gnb)!XubB&>>vFTN7AD{V@{75H^roNQZDZ%N^1o{3g?fbO0CR(H>BTu-P74_8$Db{s7f!o`qR`{MXrRaZ09J&=mf5CO5 zZI{elzkXo7LOwXg@s5;d!G7E(XQR6$E1Tboc%b2Es?*mkY;Kk?uE2V+=tb(x+5LG%~KA8>Am-ta^c}>-Uar! z@IAk5AQN`)Hyy>ds$FPow0)pm2nkbNy;a=4f@2qb?^pY2Txkf8{^p&KER(2&5S(jd zIr!oI?XN|1x=R|f5OJ-F1{g;-5kIb99xPoPX-pn?MZs(^`h|Z)Cb7WIOmKz=*Ddi9 z^xaa_gN0{3xc0~TvRpFB0U9?JYtd5-69gJ>vP@zr8jAVJk7)SShw{K{EydboAwB9$ zJ6`}gT)qbIGwpmJnFRM#pJ12)P5F{#5*?xPA&XzWWSPWWh>Q)uZ#}}o<+~zOz5~f5 zehhwhno5EP%g5jCiU4sJLWhiNvc@qD-97lBulU{R4x}5Xz6kdF6!Yyuz)pJ98Gb}> z*5gIIJV^b*JoFd#N#hJZLmLc-zZ)_MbkC5pMLra?W4JVIANHicy&d|6YsUn38&;?dE1!gbHgIjzfOx7qU(t=ofyVU-*H3A)W&)k^NuM zFXZk;*0)eGA43}Db9~PZTECE^ic0bhD#!cK^y!0@DtrT^Ib9>PoLZ@%-8W!E!b+{9OcPj#tednj&h&!+NnUhh@uQy(h(I4?QP7+i|OSe(~aNyaVn@vlevU zQ35{DS;VXKTGk;II+3`K*ShhWy3>>%3LcTzcAT%Xm}!4$$K8i4oP&!xLcj947p+)= z`jIbfJ$Xaob7i9wH%=%>q*&V$KN@*wg7G%W{EX^G?gsL@o~~t=OZv`QuRV6ckJ(-? zMB_W7akkf@UbZ!+8a-BacTc9aVMM|f_GZVor(~jB*y1_Vim$`H-O{t?EKg_CD>T1QW>-z(*6@RX839fOewP+a1nUanB6p zWHT=r9EkZ3X7v~^?E5$_s$$;;ayF)8PC!4`12OMI5*7PC3opgKJZNY? zrel7~sEMP5e>9_oY?R8(;7Of^8obz-c6TWDU5oMsrej3bFxk6KB;!iPO)*!!lGpLY zzEfBRU+i1WxRbn;IT_!yLHT0e9H#WezJFm{f3fecaTzfk^E1ZzV&4mxFE|i$6bloW zjuESr2NC;T4t^r`m6r9zz9KdE#lH9PqQ2OdWjQp{G56r6Q0yy~b$rt?J6ZT-vF`*j z4@}4GLYhMm`+k`$eX(yYdF{t^%(d(%zSuW|_r@3d-okvo>6pJTr7!jsn>xPO_gLmT z46*P1ET=E_oz3RmkJy)|$^~NIAF(XH*!MV=(>ERSJ8}t3$9#=R4=(nNp$6|QvG1o* zV-fq_&guoGV>;OqMz9;nUCmfSMsrW+oiJo(u83a)v9IW41!7-08!-_3UPI1-*!OI< zTp;$9eMW)US0sRe*!R0k9GH&bxuGGk?_Innf!O!MBm>hiF3TK_ ztZ>M5%!|BoNbLJd#)nMDtYQ4VV&4X`4T*gn&?5Gg0U!|jp3iiF>6lNGJb>6Y6BQS+ zZz@|LFdf5Hu#oAP|7M{MAodj-Um?>mQ`kg-*mn%M1!CW4NCslxZDbyZeaA3wAok@s zts$}RvCJEYee0MvFdainI3cmGn4Jm4z9Q=niG6>@%ptMwT+$)aF`LOPB=%j!YlpD0L~C+{+p!ajv!VCTl{yx&V4GdnkC=A{WWid@b;9@}e9 z7isa5W9fLcsJ#zmt!Gqp1!gS6{Za z1EzN%IG6LZ-fjav+(zXsyuK%G`RprUuaOQ{Lw%FCXw2u34fwn#CG2g|5^s|YpT;!} zWH-Pbw0BTUjtpoldbVO+Du6hvN=OglztOkc!p4e??C)6yl&%=ed$@=242`IwIODU37T znFug`#^vF?&9|a)CN5cK{sQVT4ry7B1If%E!$Rm!3=^a^-ej41WF+R#h$lZHrw_z> z{7fErt(&n(8^|=hbPP|HnP-9@Wsc;BgKODmR9tXcs2s6&aCCkit#)K)~fn?@? z1izSJ1~mNE!yjr^s~}Xq1If&fKqVe!W2hrP{%%*)VCxxFG~6!cx?K1{pK3Mfj)0tE zFf#LrNH-Xn`4R|usk}+?2iu!PC9Y_2=$;{GOWDi( zXD?hD)`vYQaBqjqoWrMaY_|d>INhWSy~IQU8E<6fGB+>icdXOeW}Mixx&>!Dsd6R7 z=b`D5ob8hbCXav(u?pAtK#u-_96gGMmU7h(?DTl;@P4<`!@-&NJ4-Q7ae`uv;&qC* zE6Rp9j2V2V4qo2wZ<~!BOx`}dbn3Lh%G+NUK;BMS`{vts^kqWUZb8-_fqdP{{%oIB z1sU>vo_!x{E9BT^&%7AvCugr_Sj2`L79&1n>>iyB`#Z3~qcZktk)hKj51Ifn_UhjB zR>;^}yYQNW+`TU0I(1#cC{L#ho#%VdZ+8Z?$j%`*hb$d(=+E{=jNH7e+?Sifbe+o0 z%ON)}cib}Mx2n4_T;D?(Ipo*dpcAnEj*@NNn2(6u+{m<}NJlw2<=3Asi&>C=^A(&< zIXUaxt#WR>#qerB7j>(Ji2b9zNQ9t;&`H>yVrS2Wp}0??4Fj1r(?+sA8Mb4io_5zT zD}Hs~pG21ZOd!iHdnP3t?cr2ybiM;w_BT|1&Hc==0roS)UQ)N&%gjB@v|ohsez1o* z1!M6r+a8vYadO5fAch|kB91-uJk;voB83xMo2N;F97aG_ATNHU-o?o z->AOqTaG?ZYVfjeTHB-S`y`YvkbTSMKVSCEnQ9>W-o)bhvhNuz!?aOSJA)i%QL?mMD{H$>&w0u zk+CoPZX#b__RX>!n(Vs{O+eZAaV(H8`+kVmNS1xS%u5Bb?_VIzA;`YJNtV9s`&shZ zkL>$&-W6Z=Ekb!;_RUp@F|XjuzDKb_havlZkX(G(_n&!%{m8!M91efW z^A$|#%f5fZ`uVc&FOqulhfI;b#EPt-=isvMaj3z2OZL43Z6mVpWxNf6?0YlY%t*H* zxh0G>WHeVKw}#BjJ(Gz8+4q04F#_55ZM{|}831r{=APvdBA7kP`_ALr6f$Y1D zi38cUoFfrzd0xtU63D)P&%}Z3TQn{L+4r4H9LT<9L*`ni6ZLfSA9Zr&Or((Pdn=iR zWZ$Ba5|Vv$J!=5jcQ&sQl6`*#$wc-o13)1Ara`-q?3a-%m1oAp8C_nFq4(3dRPq?;_r~K=%D{7A)BE{39j~ zWZzs749ULb9IIf<^AhF_WZx5*I3)YtKsqG*2H8*cy_F>i$-a+axkIw=`(y#tR0r??o)n;mE%EtQy%jHR#?k+4mMqa$p`3|2}C?X7j>* zhUEEq7ohI;CQtSq$-5e*us3}#4CEl5*2qJ_cV*kMML{PrMN6A=VkW! zBXQy9y$g?|D8+tWN_Vzby65Eri>%n`NNnWgqbNKNHfn#Hlyw3Be;_?C!7(4rE9*<} zTUL@JJ;&}+G}2oZonLF)P0NeUnpUzL#x;%DeEIUC5(vdbXdXSkaAJvjaj|<;#r%?z zc^e=oFPS^ft*Q`F`Dq}K8m3b0eeG@*upg^qAA!7lUt#&2tT;y}I;M#O{e9)|kHrt& z9F7*C=H|i;Aq-bhL2n3Z5Xb-lQK=%JGN}=n954e( zASIJY5U^GeaHvzU+7|n&hSt`hwx!lutF=W-ovJOiwY9dd4?(JScvYXTwtsE?zi;ie z&pEkdxJ017_t?p~Yp*@5J@2#5UVE=?mLerUS5kHRxxfG)K4EzMvWmKuO%T(6h;Fmd z#a*uL+KWF#9?H|^p~c=0C<_nib+7opCKulayE`!WJ85+7`i7i?()H$5OVOXvk9mts z8@8U{awzM71LqKB?0MbIhyFmiPI+ka(`U^+%{izlw~wlF_mHuZB?`YVz~P8Vm$8q6 zLm_;I;=wD+m;Lgagg96xG4Bxx8H=XN*t-kIccG0#G7NWr$iZ5trG?|E0KP4)vEkBX z>~m2L?t5i9dXlkk!Z+iimI%UHYr2g6^C1~FW!bDh!s<{QSe^NhVb7;UjjiW&8T(zJ zmqHlkuRk73FI~od59qN@t+DxQhC`?toNb}}^(15e8R(@fF+fXieHy*zKo9HYlE0p0 z?EeLNzqEx^dZf8o?Eqq)ML{!>v3nWH*i#wG*ylpVz7`px$O8-ZQqL?G>(2INSe@q} zV_!^*8e1UsGG=F7+!4#wk?CY(-zCt!rRjj0g72l^x@9kS9#F^cIbcUlt ze4Y>+30SA>ds_iVps%tHJshy)+|jGPUyK3S7YjI=;Q`AOav7dD=P^RgD}+lF%3>ez z4T@i{P?lTZevjhcR``U%pD27;;ckV0QW$6c5&sZ{qZFR6uv%e@!pjwMpA+MW!aJs~ z_(KY%Uf}*y#dj-|dwAf^d7Sa(9v(o^ZwM&&@Bqp^Jb-c!51=d+0?Iu+fM4`Xe7T1Q zxZJ}7DEIIH$~`=QIT*Z5=WvB`4-as;hX+vZ;Q_2vf4PSTI3)v&_n<;K9|Qciic>c^ z{Y8H(pvdx#dtQ10P@yWGPADEIIH$~`=Q)3Qu_xrYaM zrQ&iA4{*7M2T<h-cc)Lg5S_^V1lrdMm$_i6!et7cAzY?B3c}@bOodjs{B6Vv z376SJRJeQ_e7X}Zn^;kvG7brs-(=(w`OE_JY0o1za=u_VSbZN7RpIjaq-=%D?{Qu> z!sVM_RpIitV5M+*0)4yIr~MeceBtt$oSuE*@(Cfo%!SJoNmIDI0{IJs%d1gofj;ea z`jwe7#*F~k7cLKG9wr&Tyu``OgD+gZlT>`+^4s(~!Gs*1c!PeOh0D(&84)ha(z1WB zTEy3ZaQRSXCOB9vqV8S}R+B9d;j+}MFI*nYtoy=c;Sqe{GV`)`!sYYP=oBvhgE4*K z@+PK{E?iDANFZG15yQO@E?-ZQzHnJslWv5|C$b;NgnD7cNJc zf-hW-u-*C~T>ccP_`>Cr*xua;myck`XH3tSn^?)b_`>Bu%%?A07E$vHk23^?%T=`W zE?n-95`4IX%RB%p!sSn~c!6+Pln7YyaWpCXq($S&F>DEo<|Ie4+5+KnA!|4gF1Isq zAY86x;BJM>9Sj@@m-{ntAY5L-Y7T_UGwB-$mp3xqK)5`CXdqnv6AKs!mtUf9AY2xP zJtSNvQxHB_ErQmNaCsa3dlxRtcR@(F{8tn}gv&Ak1j1zzI0gr+uVm(WI9UCA)RG97 zUuAU#!e!a-5(t-HWp(u+Tz;8_2nm;uB(*@ed<1h52$z3EG!QONC-FeIJdkk%;j-+P z353f78Mj;E@(&p|5H8D>lJ14eA7{cn2$v~;3=5a5n083G{7=RY370QpFA52lf5!Md z370!iZF?hJ9?A>^!sTm-_C>fXht-#RKLhi6*i2?}e;uqA9ofG|HM!TgJmgG6PTJ_x zUT>teMxXY2)u;Ur6v|zn&p^IDZEBJU#I&WvoJ0BhzgA2ed`IlPs1;lx`W1Myt3wL4 z!67?TvgA7MWw|*62p2T6THeOo(hO>n13byZE( znzWlA;aSzX4E~{2xc?>c(TaU^Wca|I74GVgu+hHG#=|lC)`r?9D^Xm!vbD9gsl8$; zI>WN&R`}PqRV-ay)y37B5T)bd%%+tsq#+W@3UrNW^~)`p|+JonV?M&Vcgy;#9 z0JHYRAY5$-zX0rOf$tG4tNCU=*6X=WHWUxzGaQ+MqZJ1yBBn*51q@4*F1ze5TnWP3 zIE=?|9}hWL>$J3Rnd;Q8K{=RtmV@hmwj5k4v9w`zs8f4l$iZ6EWtY@}&3Yg`LQAh2 zb{hxQjCE=^fL1_z*uP52%Q=rEUo7RvXpUn!bd*h(YWS3t;xFc<(5Dhpxw(rsV2XJ_V7B#jUgog1k z9&bV(RRTxKE?M8qDj~}nrk6s0nE*FyEIo@`7>=DDpx;krx7rybw_2g@7V2 z1QdB8pvVgWMP3Lf@&hi|0E;$io6g|eUuGJ0@|y8#6xVi_bu-Si4tw-U+4SGnMCU;)?^XjWPj7p2u^((P@EB zZD%Xa)aKnP`n0KEJFf#ewYNg2HqQLI=4ZA zqC>k0{?JYR(yQL$NNOipO#3>!ngS`XcckIe>Zsgh3Kjb*KzaHqwHhy&H znPX>d2k0OUbW+be^-k!`zO_re*`twWy58&r^j{Z<-t6;yz1i3jbRF{YdKUH+JSsr5^41 zyYRk8UZ67@XN##v+s&Je^Tjvp?uRqdanwgxclJ;{W9)21-L1ugGsb^pIwda$XN=2V z&dNg@cpcYypT`;F&qzMN^BmNN{jWMRoPJ2yxfjfx;Q^zeT&m%T+ zK7Qp)@Q;zGif!{q*@|sb=))?ueE>mJZ2K!%DYl&h*p1ls6?&`kiEXP;brjnsnIT_nD+goK#kRj-Qi0f(=Z5z}Z2NWAhcC8$oV2Rn z7u()KED+l+XVBiowj)vU50}{XHk4Szwr8>#0~6)j+ZZ?y+lmA)5Zktrav-*CU?u~x?L4+gAhvyjfdjE^ z7AXf}+nX3T5ZiKOhV@}TOuUCP!OyTldKcT~kxWQz`!(cA#I`a41Y+B?Gr`jttp~9! zj{=L>Rz7C}vF&Y)7l>^?#~SECZ2L9~5faD;?9?R+t z#I~#0G=bRmhYZ}U*!B&^4aBzd7`J<|?FQ27L2P>+sqMv?;0`7i65GDZ_&tejFF>{J zjo9{CW;hVr&SE|GMQqElYR?2OVwlfK)6oMV6KJ1Sn z7w!~_aj6gcKg3UVTtXlAH&IM?N;T~uB#z9o7dR9-{GIYYuN zFeigg+e07r>GbyXVG9O**jx*64N`8^VIPlF?eSjcyFffgwo?DEl@#|)$l3Woulc^+ zdr5Ow`mnb;Zry~_=0G8KBCHU5*`wp;ESptaY_(y>U!^kaM^zd26GjjArFY&1Ag7n3EPu-tkSaBJ8lyE)f8mFv9lhWRQw^Q&0CHit{4sN&w07@HKW>5CY9|LU*) zP#)RcL-@S!4(k42JDl6;SZ&|NbyyF(9o0P^&<&4@11sJ>s0zESxng-$Q%wU-2)9hc z#tUpS!9jPP3a)6XT@6`0T9ZfLt!y8gIjR^~6ff{pu^cM4I{@k$soiPCqJ$<6F@h>vu97Le9=c zl!IU3EJsgr?kgcr*lLL&thJ`gxgW+$*~*a#ZM{^7;=t<6fFN=*Eoy8%Uj#gyzpbE0 zxghh0g;<{+TiE6SR_8L%8%c{A>z6L)-X6*y`3{@EbUF7OpqH}504=@su!r;aQYe2t z$+^D=de7QIDm~KN9HI@Yb1e!Qt{0o`eegq*wlECMR)j;+zOmu3p3QK+`7Od_BIk}{ zK|T{X_hj^!kNDJsdI>p+)#iuDfsk{v?gQgGtOu@w8WYY9>xVrlF#AHz&HiZ(`zz-G z8qOt@dzTtie(CSaxqDZHZP?kcauh_r6%Tb_->;BmJ*W%&pzK=~i!fIolzn47pdJqD z!Z!6_bzyhl<;8kw#+1loE1b9XLPqW@!|qK+KDo54EG4G0{}rdrJ>8WreIy3sV#iK+ zMt-gv8F~AaMi+LZvV(HlUC?j+O0!b-tY2jmnux<3J8W!xU@D&u~f)~x6y0FZHCfi7lc+%F+kNXC6S$f}I{5%_c`<2JFP zpJS52ozLH3WM9TzLu}-H{ECd5TfS7r-9*Y(#$5u|T4mha9i}pF${Z-;z5uWr8TVWC z@@3ptl8i6ot_t~OF5~9WUdp&-qku2tK7my_A>WiEu@QL>jwXwOQ(wk?I`iPmxaEAB zFXL_?6<@}E3`6=d?uY2tS;oy%j3VRSKtEr`-NJZ*jC%|-6Uew9VX1pLn#|)_BIA~t z^<~^kNO)1c7IGOA^<~_0qVoJ5aElaNi+!lk6PP((l)MQ~zx9Z8lrzX37^TyIr|{DN zC(5{wW`=wj_j61mUB+F8;2|0JPZ4G>WZd5;Nngf&6lrxMhh8Mkaf4P@MMDm9RC%c0Uh#=V4X63DpUA>}~EEn7YU z8TXeMIFND6!O76kiNT;W$0`2Nn#-d+&7xz%tpX{)34|*TCYDTLFONM3SBme`y!+D`eyogH%E=y zYr!DaZefZBImfEK{uy(7EmMD1a?6~OEi-DbpEM~OwbxHd_7!hY3u$7DF zbCEbWPc2(roC>-fl#=(L_If4H0;85bu-@zIq4#>iXb8z60uS98P0iPH_SAe`GOl=R z=^SdmUQ#D|ug7Mp_nMug8~M1e_nKiiulwh}VIR$2iN|sNjBXc}o#S-Pr{JWAZ6run zVs!RQYpj5g2Xj7jH$wkklcGc$8)>O4u@pw8fSced^ zuBEvRhmy}YYe8yyYC=(2QCX_6wHBwA+iH70om)`{A?XU9XNK&wc_koEySLRY_4}+i zEXUoyffnnf%d5~iTWhOooP#$^A3Q;RP^^xjR#a14y>eMa6?P*m!-!NA6Tefmw7KyE ziPgK*eI0&ffCUtjE>|B0heG&pU7BwghNB!gU9SFm==B3{!{z~|%hkIJ$M<2F1jF4Q zaTZ$d)XFJ>5=AUwF8KG6ofL7tN$0mWg=IHM#_xj z>Qf+BUyBSOc#pcTzYDqg1)5g^TQ4Cev4-6bgj}5;rGas!Em$67T!;STFD=ft;@7*y zt&MPh$lWRDw}$-%-NrY>InHUAN6O((GN}C0{|xnGIQpM0KIVu1mZtzeGph4#)kfEK zSo3voclAn~CGB*biRGp{om;;aC)J(zsgcer3L7{+5RUKGKI*?-+0yE71?cr&?D5A>nw;rgfU+!P3hYRk z0CQg6$=kjgS{HUjcZ}POv+^uP|H2(SgMP>E_w(GHk<<&XrSeYQ={bXOj{do%oA;iC z|I7RDA0FLt`>RL3U;GNm^?1g5ltYElC*yq<9q1$yR4$!mluO5opiTOeoQOzcseO8o zJJ_8HrYRfxpF`@P<2XnhLi;6<-e6R9|G*tOHugAe{8Qu%jR6xyVC?YZ4@@B*_Xhk} zNi1J&(X0sHc=4>r6~wYFI3JiBkNn;^;ulh!XtV+_f~tU5G)i$=mbgfY*^%XNh-b&1 zraxIg+z@yk01fltF3GNP+@tB_UKnq!ZFk&b1u5@03U16^|B8y$jypCVR+LNxQpeW8 zioG5`1n`_M@poS>IJVX|Epz&|HW5Aw!>%i9eD-9ERKBWH9P38b%q}jQiDkOtk9K!H zh2zE=*3{hGC{G$XVD+>me*qgC2(Nu42Mb>Cc(j=X=MN)WV>aefJ)1^qXl`1Tk{>$y zmc3su@sAwH8c0CacPIT$NZ%?M`9=#Bo9v^CyEtkPYHlAcwK8%V<(4}mDNpf0?B~Le)-}yyf6D9zc*<( zhlabD{Y=8AX{T?u*D;~S;l_T=`1V`yXvOi#E9NqV7BDPL`ZsrX;mBj#IE=?|pP@yK z^_vFV+Udnflq1R->zDq`%{!^KAP~#Z(>FK2^`EiC04=@rZ|)Avind+|ZM{^7;*iXP z7}tfgsIm2&{>^<4=<%C^`QwCZ>7{>j{|5Bfo2{|=YlZ`g=$nT_`RnPM`){C^@?!<% z-Q$}(l)s+7xer5!z>x9{t$a2sv>y2=U?$((=OJ7s-`qTJoXKAHyUbllOpBe6LU+nlvWf|WElgdIS(0$7U6rw+) zHB!@dx=!mZ^1`n%B$>>_% zms}-y-^BpneV-(jZNX+>;C=sK9Pu0QzP}T%Fz@>_dS^$z4glVldGKDGgx_rUANX}o zbjh~SFe~nocw4FCPEsDYZ9MGEiF;1G304Mm=k;sF%iTRmkaF{)@*L@22ZHgfZ-RE; zhKAzm`ljNdBHrX&ylm;x;;PohaZSxlwc~25s_TnIy4LA}XX&%T1w)2bQ(ITG+_ZzT z!H+v`WcBQlvN30lIdg2ugqJaL(4+W{37^v@ZVjsoOPJc4tmW(QU}j)SuyxG<+xDh^ zhyljIMu_)5C>N?_3>e-6&PQj?0s9qoz`5W51)MMWS-urx>_#wUc)~r4?|+(nJPc=f zfNSX(GW4T477tj6Uc-$=7#jyxhx9I`MUD02yUW_?#VyqwA=X&G6X8G}#hSH1I)Dtp z!&d>z!F-WVwQ*o|UIg+oEo!XaY~W#fDNJ{aPkMN7`1Hu9+BmQ}ThjPNTYu-nFHElx z^vDm89^GyJWcCJb&34f1ioaWd0AYHQL;1rxj8AVp?BV?VXIlQOAE)3jJ+9H%{NdH^ z)BBXtD*>d*MTf|;M1a%E=XQkvVuqlAUGcMDhA&98XfS6E!gW<%zR+ynW7v9xd&out zv{@iDZ$;y|oQ(#@p#TH%u%1~imNiUoIUEjwn>Ci6#VriG_s-9TbHen(DH3OH0uzy* z8pg9lMijV&Q)m2eixFqb2PPu7t%h;dKwu(L6JdOi$f)STIe`4DeGM5Ijz86A#+%|L zC%h@(Xwc(&DfQ>VmkNbl&sz1=AFAcC$;^xoRXV%xT)#8bMk)tnz!|aol%6xy6hu_IjNga znK-9aGA%1_>SM2W%Zuv5Yy@kxab2Sg^PX8;utjy)bzAVF;RRc}T$|2|f;9~tu;D7L z&&_g`R+nG~3$_5YO^v5oCLRqGML{DS*h}kKk8>WktOvdS%C=SJL&j_`y2w zTG}73#BVm&P241Zj>3=6(pH0|Jwp7-O2 z=6?)$P7Ah%xFaRRg#*|dITCVOuu6O-EbhFViyikEv7UqGhz>*S+*p)}52UNS^bR0z z+}65Ow|b5%Eh;T*X|3&Q=Vx)-nl>Ff!Vt^$G{X;9gzR`lNc%=cx~CVNRXVF=&gjPC zWnL7FF@|y{zL?|H8qPs;bK^I;8*rTw?52+#jVieX7)Pvx>}qwGQLU+KZEpO43^6B% zrJ?&^I<3I^NH;6AZB#bWr`>u!W%lt3OQ$naE{O*GzrWT^Whu2asgQRkYEpZ+A7xql zatQJd^s@2)e8&01#5X@|dpaNnhZw$Bt(^{2fpFwlW3B0Y^C-+-oI}Z%_QW^WVle*F z5(BjKX2Xuxt#94|y*Mz^>xplk0@kR|kAUI^wz^}YWBt^n4k z@fg0@yfd)MD^soTov3@h?>r0y_7U=(tY1RmPvL3#<&ks;K3ZXkLd!RwrubZi=PKlQ zVZ17Z4GLE(T&M683U5=`L5O_ZrSJ#pzFpzl>P}9K;kGK2{G+c({^2g=#E*rxoM04| zVy&&s^4Ptt^_EVas2d-8=dZW0axiqu1Qdc-80w_*u*V@~)_K>K-u#;9ocoGd+g-bJ zh*Q4nZ|}eVGI?g^k39I|5{AEJH_!b$;Kxyv{*y~S1^is-_@~He{;tU-cV?kn{h?=I zR|)JG!u>tiQ_c*sgomd{!?|U*<;Ojz;}z;a__OM%w{r{VId7gZ?v1-z?%bWX{mh+_ zwa|&sfv4rh-M4PMb9a30jl12c9lN3(H|`!#3B3)VGXOdj7M2bEvjd$CQRs7ESf~6G z61E0(sN11aIFzBh9Qm1g4W6rZ&valf*|F=o(zUO-9oL}_&fkSK?3bOq&%*sS*43_@ z)LPir?vCfJ-5rHJZ~Kiq-#o*7Td-=w`DeXtnSr|)kbmS|$~gpicBt#29C=5cvuvJ` zm;S(+mndj%nW5h+P7Bx72T{*|3)+Bn;h}se`zyDh-b!(|LJYK62U&<8t^?xCUyFYG zyovbCi+oEVJnQLN(6}t5gCd$TbmU$J^Z|_7N6@CbhdPUPT{CF?j%dlv$!Mx_2Oh^O zSr4^9tm`Y2%=4R?4)t1C{CmhZ>*38)$Gs5`r@gl0m3pMhJX{FK{=$06*ZzWahW-QK z$2$-z;NKthb}Gu6US~tg7wvS*JMgMQe1QvgRhHbDyry)0UbJ#OM0E3a#heawy&Xg1 z%;TxrR^>PD?muC@7RHS4@LPAFY+dIR^*@#KojM@Ww!_aUuwCyXNYym z`eK>6AL!;T+NuctY^$sf&{iWrV-OyzFC^SnF8WE-DIbLP!kFxPd(E@$g}%b}7>cs7 zJyFN6y#3Gj{~&cz$vI<3V_e1!oavgxNS%_+Aa=vjziWDXzYqR z7=u%9+|9n}_tmMSHy?h^BZYY!gB`oB&c70UZQbsxPrdTBl(S*y&`|$kdliBn<|pY} zxeod@+X44uFr897Y=<1g;rvBCBRMF?^T`g8X=kJ9ZsYe$~!loSP6YJicvSyyXZ}dG*d=NUy83 zY&x#f!Lk1e(<+yGxmo7gnV45u4&48N`4#2ixGGt{V*uuFb^kQx-Rop*YM&$>)=duT zmbzXzhhd%M_b7@O&hJQ1=9MWagvsj=-8Ak=Sc5qZIPx;KWW(-gYJ)#_moUy$lfDg4 z$T98WeifEqXkkvp>--Vv1E6>1*nk$xPj|MtDa(yJOG5Jq^UeN8Xzb;?IFC7%NCW*7 zbo&n~N1CW_(u#)KEjsmT%>Q_V_9D_DbPGn%-LtEfIXXgT2?~9QK);Y@pnd$Gt{U_`fj@PB!w70b? zUAW5P2E*8)Sg1-QC_}}U#emQPR2=$}hA>FKd}r2>V$K>vLSYj@ST369m<6x_b^$Dx zVY!{rP#%cTx|+q%dFq>Gd~uTDSiH>#ClQCc8}VI|g*3s|nWe3yriC0c_8}qKS4scz4ml_NTfjiEB!7Jw^oM|HivmkL(!7%G1R zNSDfHO{j*>6L;M5&6=owJ!jdmI-mA7Teo zkYC3c8{`9LXM$i%zNknWnKex^#huF%Y%VPc%Ir9$p-@@jEvVy_JQ|1Ov9nt$O3^Z< zq3A@-n4*&cU#w-5`JLQ38M|}h)ZAIv2|ESd<-{UK<^7%Vb5hvZ#-9?T3+0gk zVkkLFzyx4O!kDGg4t`_9oQ}sm9X=i}ZotwhCM+(UW}$Oo>9h)LY_N1X5EzzDx#WnY zQ+@%&v!h=@gm|{BnR0_>G%M@th}*B)odN&YXYdov9+|xbb8&WR1+eUYAhqm#dN}bf zircCSkb)=_2FGJi9s%5=zEK49nXsS znnaX)^R`HU$#ngOSRX-ty(ecd>|p$OKN8n9aE%RN(z(AtO5Z^EVX@H(UM&|pjxJt- zGq7TX6B}cS7EOE(xv*F~F&#OuSWe>WjHV&;24XXCZ0v7@#!!@D6>RZ8;)naqr52GW z6|npP&!|{w;xEWWY`m?)7->zgsV+vGSee1%i8ZK#*hGWnBxW%WlMI%Z7)0!NgAGkQ zPAZcPHY_oac|XBm!xKl7%83R`IeA-=jb+TE8=IaO#dtI5gbKj81a=y+2sCvh7Sr<# z76Yi4_!{#z+n`w1jZ?n)I$}r}k8<)3MJw}ui;Zqm6 z4(!Cy9Za~n2G-oKB5TuuUKsoGz={?pcDF_xh%-yEdo$Iw1-LoU*z?J^V2K^?kt(`Ik|(HQQ@npl z(W1#Hi)PVy@?P{eAI(WNGw#V6H!nF7G-9WC{jgBt6%0-CGSAql>RXar!@$!#7S}5% zORfNn%~0RT$&K_q-8)+1PDxUeQf!uIrkknBZOqkd4Ll>ckj*wneP<>=LsE0SF%oxf z@@U3ATLYIT|H8QC9-9G;mHZn6w|cL|i7rlZ6vkG19DR|3r@iEQM!v-3z={++=Ox>S zukk(t+L40aWF>inbnJ5PG1O0_U`JN6n)nsoEb;$i7Mg&yomI->L|oqTcF125_T#YR z#IoI_Orvp^Zme!z02J#dO^DY*5?q24>+hP{kAoP~84&#jeh)bXg+3fLoEsbIChujy z0+((i^9Pa{rD!bKj_|RgT~6m1jZXY*1S6sy;?QOketT@Wmpp={U+B${fKKu>##^LS z6;0kus*62__6p+364v^89_MSXU{sQKxW>-cYMzUrno#NkhleZ9GrZ~DXFKsb!}y`jp-%ySGj_3;oWlBS_K34te#(Sfyk82s+KJP`X-VjgaN_KQtb4_W zapb&-&UqL|C)$VYcGw73?8P3_bmE60grAdP&fy#{?hp=WbQ5|Gh;qyv&i?A!S!KGo zm*O{m9)8%Hv?t_JjQc(!Pl+6!_z~PAM;s^3>m>e(qC|#MGV2w1iSdAu5eo&2CSIc7 zkyV1l6Ejev$Wc=T>z`nkjHKoWmXqKCx=6mEnVWzt)QOCoD1Lc~*HOsG==YF1W{bpQ z^gFIbuwjW>#v5b&h9|gA85wK*Qi*dJZ=CTPmAD<$B1OipFp;8PvBAbBo@TrfgOw!y z90gX&5@Y^x;x(|)OR56LmTv_9NkoW8^AZygG&;ycbP{)?Vxof$7IktTLcq^Tz+~cC zls$TgPM(RMGT@;Gi#kXA9Rb@L+nl_tyu=Nlm^J7vDV~$K1JSYu8!Vb=LSeIp7%ZN+ z2}H7n8Z5`@&rg83Tb?)tQQb2&rGe0l;<#tol%jt|CAj5GZ}<{~zl!K0nN<+W8Ox|k zM$|2biYi8dcBy*DT`bmeXZQxh7)R$zBnCrw5H&GYO0fCPQA5BrxQmf-_bQkEHB4j) zDP1i=&QHCGN^w6?h;ZJ`PX5nH7WS#aPtu$j(N@DqdmEhc_?HaM_1wpJDVMMsgx&j+$Yc}+-e3G zQ`Nc**4JSj`)lU5hOW+V-Z9^S7IMEUQCPIQq7rSkR;sU?|~5IlI{=0f$8x$x4Xoo`Glld?i61Q0h>FCF@G!p z8R$(D@XO5dk4(T3vi0IQzUJKdBOvp3+~>t-!I5nEpO{dljb0#4w2|chX~fup$K-#3 zw$HKK)!8-;s{AvEMKW6D#cwfZWZB$TC1=c418a53ILX;7CVi8$oszSKCTF|EXMQTi z(7Od$bR-T6{!XkY^fsI>aksLw{Y6NVEaw?Z`oARLVng~JK^BgvsmJD%{}yC^iR`(4 zmxz@Auj0JGr1&>M7MaTUJJVu^T@T3qcR$kFf=3%vdZ1IV3BEkWvki}JQK>(C`8mVz zgo7U&S4SHXwfA!nV7fZrlpXq{Ala%IQZPg{`?w)D#!@LY9uEmG^{Ys)|87OjJgj!nsRp?oN}f@`>{}8*H{wO=W#SJ*7|PAr`+4I^}oa zvHUuqbEvtlVxs(*a<`a7nc#dpHqoyuU1##I8I%*V`;hB02tQTbB@-~yd`ko6KZ7u= zFnTY>ql3asDhr%rE8*6HC#bNGVCliV5KitklL1ogATE^6z1i2MY zS`jy>bEt@QOrf0l-fmJL^?UG`6n|Nx?VDVJ)M7Hg3ddk zwyDN(UvmF}dLuXHx)a$G++Rs&#)>}7#QyYn*>vx6r}Xb6{utf0l`1#=H$g-85fh9+`m}x{j3CEZnCidk>&GqEN3iZU;o@ZiD6v9#Al<{^w z(cQ+NLk$JSzDFpW=+0qn9B$m{wnf}ea{q$K&P{nPaemXgOG@8lPHv<97!#iEWb;v* z?cC_+Nr;6L+AkICFATrPylD_}2cAw%Hvib|>KruLfGC->0a3DKr4>U#;4cW{&hXr~ zPyi-2BS>(%Cm|M0A@k;*<}rvQmxC~)@O0{f3y&SD&VCm}LS*SmKgom&S;rTck};9F zcu=xBw72=zanY6!GvskhuG)lTsLM=9@MTrn7Eb9~47i;nSD1hd#MMjmsIv$@szJvO zX8{#E$N9+5(IBy-B4q4cH}w$5=R|pdE=j$}K6D!2Bv}zqc2d8>k9)dt;73C0HO4x_ zIAAQ|`w#1<nl|;s^8yh*dC{k2ZG7fNU?b*eVFJDm^WtTo31FD8~K~F z?%LVQ=8P*|e%83+FB~^MvZih#bQVMg%$~h`4m6G;Y-8lb%V$Tv;ub}Id3jOfF?Zda z^~)mFb&;1ZFPghHa=SYld5w&DWKQucCHy6LM0UEC@JGiLZyr+`xwH<&AmIj-rmSRa z={V>zEh)xdN!jS}#gUyKFN)mZA$PlAxyM_F`WRna`-wTrK00SvNpa+RAFrDmdCFV& z1*Y@B$BU4}%ig*Pkvl(Lv^?^fx2~~h{DfsfIkLJ4lp}*@kBzhxMTQhbRXRg3Gw3*{dp(b@)$(-VAsVuT=EC@b&8Ge2eSr_^GWhiWCx8s?i`*=qg zXQu;jo4BWLalXR2G*aP_+{d6=w0JoE%Z`wqai!}5+D+j_}3!6 ztD{$CZNSW7%S3jRBA*$#Q7M+CP5yGKxObg4`MtH`-#Kj?pX}QG#{VYQW^86|q4g)j zloJ0O6z`_!dUu_7rRD|B!c5J#{-`UK-R7ChOE}NA{>YA`5Qly=E3Nu!LrwDalcJLn!T zRd_*gaiG4bzP-Mxp}wuQqOlqF=2pG2vd!T~Y(?9udQqFo??aP0jFq1a^k{Cumn&KP3bB!$rt{K<=C)dg9CC%Rl5ef3U0vVq zkS(oPQQy$ukd>r%;g}3uB)k9n;Qh)5x zrY6~@#_;8DO+`)U?oYX^Q?fz5wY>hbY%)sNSlha+wrS}a=$me=YOyzku4=7narD;E zy;t{Q^z7D2KGW|HUA44L?h|d3daCX2Gyp^;b>?uSQ+k+`OV*u0>teu(H;vt8c1lYiJJX&oMo(t-YeHrK+`d0y>YmQ?x8+HmfoSq|3h8k(0O1DCYR?X1mp zb#2g@B#BovR{wmu zuy0>d)gbUvoD65d12xLEt_BIAajil+MW@3Z6|EQ{NMa?1 zdqq=yb9I{oD{7{P`HFe2s9V_t-P6+gAq~_`$bR^)X{_9$_E9Gs(`;iD0$E0iRj7Ba z$pkZ8`=u3)^-a81c2zAtz$#c9@W$bks)h<_>_!r(GCirK3dd27V^3!V$sN6B5|oa+ zrj~lBS2nF|tF4iFz)m&N7o7G>M3J@J4J=pu^0r{Aw?@aa3bpNQ8_b*-2sKbgiOJik z!BA^={K3W&0%vJgn$p&R-^FC#?x4w=TQNYWntSfq)8|yoJ8k-cdFRercv{7LzimsU z8f3Dzb011&=2O49%=sK0QDtbY+EpY_-_#sB)Ne}9sTR3qr6ELh!wOSD(!3QFr_Y*u zn!~|@>73cNmmAv|98s22XD4 z)#_C>=n#6zGG1df&MI_$NpflPN}g1zFr6IDfp^@}s+yWs`i7OOs{(Ums>);vHHsoj zZ3IP=#=~6U*A|BuYDl^Xw4uwig;!C9iOoip*R6j&G$xJ}6%AErvBoucDPY{H2K6qq z3sfMV$ohi0&>bcdCNx^rlCt!GrP|>;z4*}M&=*m4QX|qGI0;Ktpx>=xi=l|KUiSS^=~7&8KpSENzj2C_1&sv4xjYWKiQ$fR4D zc5_3GsWEelw&gM4-6)mM9**0_Wk5@nGvDYE9LMAnLRW61U!rse{=nG2&@<+W%vzqL6$e-IVLQ1`DU#MM~sdp#%DP4vq$40j!yzH zKM!$Kbf6#27(5Ig4!4CKYHYeCz-=6Qk=_|LQpj&w$j)#_fHNkCY>lPCGija5aT54- zgk@0Hfi1^8*liqGoy$_#=FvsEXTdK_Z`0|fT%^bHS$Y@2PI}|<&}=L)eg-pk`d^GE zobIg%_l{vxC8({JMquG|JCH7gLQEIM@zY%gdzjv}$2bnR8(CwmT&4`uYnd681D^{% zz0bgII->|e{ zb#5;1`b^q);D=^vVHlcc5bj80m0wE(pCx|ZqsMCq_l$%vKQ)0t-yoz4tR|;S`vj?G1+Bp1K>@qXQE`kF?*PW8n;? zK++yT2v@#c3w;My&M&fj#zbX&GcG1zKR$6|O#F=&1%3ASAeT{RDsYYqW1^z3+vX`8 zlK#{;&i>p%M2w+NX@SK#uI&1ZEe~<)UjvI>pP@g$&6oQPYRb>`7GsS6RKJ>yKi68q z{=DqW;>;h{Oe{|N+d??~xn^RFp+7k*!0FF*5o3(M#aJKp0Rmf8@*nk^)#Q(B9mbgW zesZCjr+-9d1`R4WM|NN$a>_D{bCd)of>VWI6Tt~s_5*9!PcE|vAn|MQbl};Bhi4MM z2xtxczk+8A+G{Ex{T~Fh>tnorxN%xe$<+!hR|o4S==&< zB4$y`E@&Ahl^>0|nAKR@$mIj`Rj6xOx{jr5Se@6el<(GcE4yZ8zuC-=QEt%Ucp8ou z&qhG6NcADYeG_rO&nx5{>0!9eCxkoOo$x}1l?qz`hnL{N95}qx%#94kIgoHJ;P43; zBZL#dE5VKK4S0r6Hgg^EpA*NyV9ssCX8;ZdkN74b=yT4Zdz^5pwGPs92tA>NfO zieF2Jc+0^9Fy01*aK@nlvo`C&Esr?zJX9g)UGF%@8KH2b!qEyrO8n*QGQypv_*{kO zDm-6dt-=O{YZYFv@NR_3MVN%RpDHP zixhHaE8{gPyiDN+g?B3as=`MVzNPSQ3bRp%q?1%wps-ZoDGJY2c!9!tg_kM3O5rUE zzo3u@6iEL?g}+t!M}_}SA-Ci(+=&XODJ)l5tB_k=819P-zoGDXg}+wFtD_n2U4>cb zd&H>&jF39M2&uc4aHYca3U5|;r$TOPWH|1HAY7sFQia^bM)yq$?^eiTCUpOy!k;U| zVLEYtTcL{%&Tx4Ok5X8q@FNQ6D!f493WXn2c)h}ZQ}}?wClqoUDbsmb;TsD3VNB6| zu)=(W+*VBY^AvJi5?`h8YK5Oxc$dO&DttoW3kr8A{IkOM6%ND;jdYJvSgi0Ag=Z_Q zP;%%q@SyBxWeNU z@~9pCPghv4@M48mDEySd&ncviX~z43!WR_&hr&N8j3!LDe1# zn7VIN{8ojxE8MK`A%%}9e3}sH{7T`g>i)XIKdU=+12fzJLZmxL;V^Zdqi~73S1D{) zxLV=o2|?#>#lNEP0d@bC!gm#VkghSkn8E>sh(AZ+VuelW->UFhg*Pa?UEyYh4=8*{ z;md@e`#Z&dukbB(&&NkM{l^g^KcxyMt9!G;b?Sb#!rK(yuJ9X#p!1mGybp}@o>up_ z6%NJ=n&FNlg#So|qt*R*g{LXJNa2-)2!EBr+Z5iea0?;mepm6W3ZGK<7Zv_N;k)Yp zcZJkx&G>l=k5X7lh;%0^oT+fG!b(EKYf!vJ;VN~%Ug0h3{#k_&D11oavkHH#@D)PP z{gdKvDI5ykjrkd-aE!uYh0_TU?`*}-RoI~Js};XY;d*u7r0|Ohw-DkT_btVrQTQSu z(*Kph9qOJ9IV;m0LJ0pu6&|VXCn)3rWQJRy{^u#IQujv1FIKol-LF*qDup+z`|XNv zR``^{A1VB;LMRoHe0g9DnSL%I@;O-HG3q`^@e>uEsqh?yD+m#PmEs>$_-S?DtoWA{ zeox(>Rs6>aUsU&93g1@9g$>dlpztt4w8N2#AE$V+;wLFyuK0zDpG`gv{0!Cn zl_*}OxO|_$U%pQep5Gx1->&`}6~9&S`xW1!_%_A4Kc4CRT=AC_e^c>y6vtwf(2o-0 z8KyYDdx)1PUZ(h5#mg1HQ1ME|xhBE~pC*=V!54u+ zyZk%Gu^hn5iu~C)zDO)P@(lo-HkUNqxSJ1@hIw$8WN~w8jBaju%zd3ON3d*ISK{%U zO8)L6W$`NTH|s3Y(6SxU?pr+UtP(HA{cotGZgo~N<91&d?WPXD09z9tvxospmsc7| z#nThUgzWPU2HCkFTNtz)X&NGZHIcq$?>Ec%QdyDO3L*LN-6^f@gmfi!H3bbwbc*ml zQ_$$&4*V~ZEOvdLQMiAZ>Bi_t#AlUnUX7de&7pANpkz2W?IBy73%9UgSeh|-7#^Sg zV!8`g0$&@40U54_7B$vy8gOf;*A_e+P1abyli?69#~+c24!E%#oUUv+=D}{`!0OBc zvW6Bl*6%FfVR|09K!VO3gwUUlcjeJ>^2T*h+mD9t z?wBP4oK`-Y6#|Hvgbp#;B7io^eXzr;#da$F2YAYl)%w#I(g?RqghWxW`1G{XUw zD6~2*Ii~4;n!>pX&s8Yv#qh6Ee7V9Fg&!kCXP}N&!duk6QE`stjC8}VX{%jbyYv#v z`1Pi{a{R;-CZ)vmuDkN{W`fwKe4kZil@(G+ln84+igi}3l--#ljHh#i{p7{*3~LJy z`n_~R`!-n%7ne9nKtfW+F5Aa*Mpn{D7Sb44)Sqvtb{n(l$%C$azUlD)Vp^B^}l@600X&2O!*=7wD9Mw$F{wNK5Ce#SRb*q*%P z6pS}c44h{<3@uM`wBmS$i{ZQ;HZo-4@IiRV%R>Y$y=ic>c6vXHha<}x>&N#FvhJH1 zK(+v5Irwg~F9IIU-+Msv z;Kuy*#7j;AJ-&0Tv1z6Ak~tt^+YPT*KY!`G9E0lN$$Nrune_6M*sl6fNb;XIL8LulYHj_rT{N-Nh zXYKPEV`qNTxtA4#2UCmm!ck&3m&dtZ1Y%i9j$W{m>?~j<*AvUOKsdj6?u_v`lt{4avntPKs`hn-4(4NW>1P=Jig&5y zF*pY>9PxDCa53D%M&=7Rr1OT|g)2c=8;5ilj$e+}Sifm-uy*==3lB${}Z|4Qc#9}eZOC*E)r=%tXRH8$^@!^8R88p>ZT{A_vC*BNJko_SdqiloP9 zvqIyF^Wfm+CR-%rm(ClmML51)tg-n^=MAZkE)(AH=?vEyUqYjH#e>^?SZMP@{6O%A zY|FreQ{O+lpRU2FUl@<(2>kq~`%Z+OAx z))kf&?EN0fvPor`t~HJ|lSH??;69Wwr!OJKhM^uDsT5k$mJlh*b=e~<-&e;V{*~p= z5zCG!xSN6Fk++N^e#!U!(K!B!SayVyx6k(_+qGju69xj?3*W@zw0hsI)BG- z3oJ1}OK)1pPVdclIFhWf;nMlL?I=VC0`^2T* zJP4$a7Byz--cydeI>bNq#NY910rKG1*tF95yK6)F%Ryk9ziVK(aY*A`ASqhZSif}s zj(fc=|Jf6NcPJ(|p6{}TRz8~*TKB^<k&WFg27bf(>kEkgN!qLaYLk#{)O0;6VnCM-rd_M}C2W z8+_k5{*hRAgz3byWf9ShMZZJ8-|;71qAa@&v-9c2lW_pq-{NI+p%9JSswtQPpd?jVdnXDV~jGoOO=PjGRbiQ&i=%wIqjjg|Q zzLNKp+x+#!SFQ%V@VS4|u;Hy8Owj%&iJ3DHll zQ)uPMUr~Sdqdu-B8l9iIn%FD9S$2HJ`xeVMfYHS`Z}Us_5ODbW>AXrhKiTtI;%H>Y z{T_boT4D-zaF2rlz&)-dmTkcWz`#A;ZyfPU?(uHpcq_5&NId}85^tnywwn(?!~Vy$ zM9z(Fg07*p#44csV=YlzvlBZw%mu=PS{>!liY){`}vt|@K+JC>UYl>aTGSk--dDg||kNkqAm(Kf9 zFG>pj*4VVtdB4wx^4AmZcNiu)>WH$2Rz8~*TKAC|@_y%M$os9%koTj$j!byJ2T^$( zGuDKPWSsxM|v@_t?IC-kRhFn{`Xu09Va(8_)Ic^sc z`}qrrKVdzYY|0Q||DdVBBd3NfD>xSpU90kDvG935j(a^>z1_>=4A1JEc}NUNLJ%HQ%VzB$~)bNoFgChMB+V1{=w`s0&m z=_j;MFtN+(!D`s|-}w~nEuHwt$N*m|%{jWeJ%=ozl}U3>GWE_?53VbcFw_W}YXN}w zzP!lbRIMKDL8OaOvfhbWc;V4T>W1@d@k#9c0%D(dP^M?f+#M&nPxwxb@TFa-dq2pD z_Kg!||K)_i^*0)R%i6hvxS9I)T!UO2;%6A&@-qj559P#Q4b!HXhR528Z^Oe8X^r(u zUxTEq_h|&`jSu}S2wY}~0a|)z!On@&nsP9Umd_)!^->MHjl-~+u0bvay-fH}u0a-% zuo|0IIv=_`lt0dKHh<}SXa(q{EHOY!Z$0ec^0tKX*ApLl1?Zh-i6}jyn?tl=b$DK7 zGA(Lsl>30gi)@3K_kd)w26;FNn8_OC3^Z0IeCT`>p8GPavH7#Og<<#q3w&t!8;Jdt zvTgqrYxvOM4bfTk=!b?U?QKnx>=Aj>I~3lp@KJ?NDeMF9%KIoX-|slFq$E?`wVVSS z!<_T_eqN*Oe1f`b&QSyvTwGb?Re?7LH~3WcrS6Ws+`}Ng!264&;2(Qw zU_2~YE~9HdHy?n8uQn`M@(w7gR3{5ul|{guGL$=j;mMq;o^f)~`(rUvn!OVXdbloS za&FFq{|xp-Zb1z8Jh|Eguvfm*Xb=vV&Tcr#56VAEegfJ)bGe5Ax;wscf2?3?#UJDv z`^Gi)U3N{KG~|oOZ*i3m~&eUSBI>!liYn+I4O>X$2|MU9!>_uLa1ULORD=F>}GzvRd35tbOBO)H&m zq%IuWZk(HJ{;mndA(wMNmJiBT24wdIOKGmiILHqkXnycLj4BQ+&XpYY zmajMv{2k{^-Jdmqu^&c5Qyc7;j!gN-^e(z>6)nd{j8tC=A7DL;4! zjmbYPqFeI{#@tS-}jKZb2B9&`U0EWI+LCS<@#u3Ci79@S$vLTee9HeFQcMa^} z{M`e3DY#i<{no=FoWJ%^{&L}G>3s%v#$o%>@P3V>ED_+e^4Y8qK+J3C5R)weXrtT* zJG@$~*HK95NQ;ERrR(bX2*PEuZn+$dmC3p#&(?QU&!Mb{)#jVXfmpX>TLvbaI_r%+ zCol&}C%ApJkWrPnS}*$KxHyn}>iI~Id}@`#8)$lwqIvn zwm-R^M45lT434A(h=-0jARuEdn{ob(7^q$t!{>GyZ~snD<#U%ZNLFMP0QlTXiDg^x zLtx-@Ij_M{mOZl~$6#Q%@o2OTu322)jNFDFu)N~|!168vaAPrP@G;i|$&SWa8KQnI ze&g=__;vH#d;l6|%w5uNE5U$D{+9RSg`sAF)Vn#_KT*}SBFUCKG9fY0ySw+y~vmZEjr_%WkRCUmJ+6JIxHOrL&Ysu1-y zIwehNN=G}Va#K?kdxqm; zPH?i|ef<0xXU=kB9(3v#wXJDvuc`)YZ#8hawSjz5d+lndl&q<0uX2j2+uE#7qN?iZ z*4j&)A_g%!DC?W*n!`92IcmfBzw8E`u6X5eM2kHpt6-+6prV0Q3%q@xu_ z_ZL&H&;o{~N#~Wj3rAkd#$h~$yNebz)^8ecYp2&ccsS~;v3}{iavbG&8i81jp4N0< z1A*<97@(z>&MR*Z)eGx~^+#A8iUX^|yV_VbYivEI^U5!QULFE5f3W)W(Bu6)!0NmX zdTb+WZ2r=D<<~>`1DoK}OXrm%Xuy;u259N6hdta6-VEihCtkS#^hzxerAKsgh&HUw zd{jgxyz&hQmkFV`GW3bV_houA=t0h!&f^Bn8RWK(7Ya?>;?;Bg@Q0pWk466|4Y8wl_k48qPp@lIbCFj(m|=cH{~GYzg4R>Ag4!zY%^*gBy1F+Q+JI zJ^&4~;4aAyt@>tHudKsD?;LuZlMS`?Kw5}k)pre{QLM?i(3Iy}uzk20UFmd|(XFYi zOJj8X)n5#WjMjbw{&qWJti!_;0J`RHZ8Ot9Ui2J@I-U;b~(-jIIoO{)F*ps#kcQ!Rd2umCV$4co0AczPvYr(>|D5o zjZ9o{Natg_3&*L<#vvVsTTP1^>o*O!wbN@G9*!<+tY11G%eCt*2-F)Ndn*WZMK7I? zoe|;_Ss%7us$sWzV4gBv^PL;w6FD7Qdg**D*L*YKW4VTG+l}*;rI*gf-VJ&w_*-N1 zp3cYCg!0!DANv&O9b$=qD6M=pD+Cbp?|30iwg{k&av$vQYO!8PWIPi-b^^j>!pByk zQZnIVsh{;A9~EzXhuAeJ5B3M@F&TqNUt zHXld?6OeGGjf6Qa8BZE!0h}rId$|2MZMfzCb2v`vHr;ca3I8a5ANIc9gSuFUhiL%p z9nZO}skSC1?A(6hGbL96?M|F!C**^(lUm8$@vD2XnOOX8bz0h6YwGLjz~LOz2v(vpmI zJ$)$dB2V8T&{=!D^9<_=4`wYH`S!iSIoKr|XgSe|43ZT&766>+E@Ighd=D7*3-E4m zIC9GJk4|xaJ-pteo4IlbTLm}?y0NUS1mEFLy6SjrnSg8ddHl7yHXvA zr2Qm*G`bb8W;4ouyLm{p;7T>=Mpj>Sd>{DA=aJ!$;Sr6x%soXM+7v{^*ybQ&RLA3* z!A=U~MFPHuyj_?vE;ag=y4opG;6W|3W)1t;kxeqwJ_(l@*IFL+u%l|;QOnA$^xr_WDZzU^&tW>y6VUt3hv4b0)&DMq4=8+G zVFMw;?**^fUfbwzW$l&MJRaM}GS$uW^k8HL(@mLxV13hZ&e<8=e&$XNzwmvO+}j$y ztNh*f-~WA1$(_5Mt+(uU@;YA0aXMZ>nBS9JkNaN3()uX&!{#OuCnvDKP2IC(2kfD) zJ0y|dxh`xA9S}O=6?fsoApP>OSGE}5{fC6YCW5e&G126h71sd_!h{ULTm(`b0Sq*d zfk-k>gJqdugObj%=oEvKh|IkNjx!`n_U8^Y+jNtf7II87hlCQz!X8UMTR?{S4fz$2=v$JP0jB`mi)WZOI`I;HkE07n=2on5yfOKPVuIRLEEZ4v8-u7{ zPGVjN%ge1;?RaBP6BIw}L3hutP;2V8YE4U>$G>s^KF* zFZ8~gThZcpcWcDlidM(FN8{yIta7}24H8dGC&~L5(JRPFj3fMt!E$pes_Pu@{yQY` zyu{s1_Nyk|u*8v!_aN)WD;S>m3&T7#L#U(@UnchLX2C`!zQFu_=aYgJCU!F3BVQ71 zY;MKMYR7xbq)-A3dH$Zs^JHMi^!H7AQxgBpl%DvC#G9IEApF6jf=$n@XaJQb4V4** zCz;+eCf?jcJ=1&EVC9K8bM|~8*if%vequhc7ao@~EOw%KjF>g}cu;j2VF*h%gy;~W zsBa$EiIOjJ;{&53fDEqb9GIvL{88A4GRDB@Z2S&p4YN;CPQz(Or_KBCI#X`_N8`!OZV1gu z^7={dWR07bJdQL@@ooht;S~%`<}&W7>RXb$kb$RpEUs5jmi!4Ta)$a&PBzf@bZ?8q zosvA2zOy_t1x-yZV!E?6@QmaZCNxKVXC_}};JMyI5_fL$4F*121D7YOh?aY76Et9w zEVS3^wMyW{N$EK&J&wpo!P8z+xRFad4z@_ab6#>Dqpk6H(QBmOH(5!Zx9~3aPDK4g z3U*{Ac_p8Bg~tt~k%B*Fp$S;qSz}n7i2D-A9I_H&Pl98Pm+dBHYK^;eV|C{;H%40x zhG()rqxE-9?Z-ik=?sh(;rEd5;%6iZp6iWtlUFfdflD`%IiF-kDH=<@L*Jv_=fpS1 ziPOSIW)07YCK){Y(ErND%DlJSOQu-Jg&to`r1o1zU!*k_P4XkgTkJ8kR}fFis65Z( zRPGgwN`8aze68)7N#4-tUEoEeddrg$#=X$HQgX65c^WA%@%ZZU3YI7NV)ZUkv?aNX zXoaGylQT)G((5l(yVgx|;oqzB4j24dH|f&9T2hV_bhycN%*j%T9x1rXO@52yY81cU zOJiPOUFC*(&sadt^If#SnBj^0F9J&a=&&Ga)= z;$f{U^u-?2bmDj6*U!l?=Wvc5cL;|!It@MlKYQ;2Cs$GBkKcQ{d#00~OeQ1)0TSrE zAQ6(8gdqtJF(d&J2snfU5DA@mCPU`MJV?S1F_Q3D1w>(8*7Y$cVis|iT@hd4;%gTl zDC+KSQE@jR#2+FmyXvZ|`}hC-o;v5=>YhnvG6|9Ollt`SZ#~bgs#~}2sXA4s#;5rP z9mm!iS|9UG@qZlu*%$D~wq@<&$4D5K9e*Nto$vC*^>E!3$PI(=Jl-L! zpMrhy=Syc+9~^%^)3h+nwUVY5X;zTjI!0^LXj2!l4o*x7fiO6WuArL#hv-c5zGm$E z8Rg;M{&x9&%(9Jgj}gZgA_wnjY3b~93oi@a7X0?ErcKTldyw4HOgwUQBWNAySN<^w zQ`PfQo95yF^o9+oiVJU!(gArQzCiohr_Uz<9ri0Kvv-AzM?*hOUY?9gXa{TY+yeBardf+%tH>IjKuf8m5WV- zLig#&Qb!{aB{|*PTtFyqI z7F^kzy1F*CF;ee__WqV+;1gIVSPzqzFccUjuDfwQ@V4GkR~4H5=h zQ0VNyzN)S!DM#@d*aGT$V=j)qnzXkzH*6(QDR$xfm8;H^2Z)&$u6ss+d3jF|9Z^td>ug4q_xJRm$6$Rl z@cqK0XldvK%UkGZ*rLq@;dsU^EzQAZ+*OD+b@kH|`|;T37>L?rZZ=D9TA|W}@z5$d zy8@T@#`aCF;YfwFp+ZXdn+oj>*c^x#S~@LMRx>hJSi7pLa|7B_3pP%oIMT>0 z$4&6r(A9&o_jmU9wlqs801Dh0$QIev4|?)S(8c?;FRW{D4FWBt(Av*RXk|k{R<%DW zbV@ZkiXdZMmXxinx0!jAs!AG{6h}(UUdR>dh+ty0f{CHly!amjeE-F8&KsYR*gS&G z0{rf9P7a@~c>cXqF2Oa7zF}ONQ*hxU#kpoN`-;c!o26ky#_RMv%(}EB-b)DJm(YyG zE0s$;#d4sKEC;zPmY4Zjjmy%&8oU8>2IRcVnDr8Pc()05p`w_c^oT~U5q3)hYp@L; zaDV>Y>iHUk`Se}{y*&I#Z!9jO$N40m-lsv2b!x`KO7{z9A^o)ck%usPx4~{{NMjEI z^R%e3up{ zGC#yR#j_NbD)K#N_&UXniaQkFq4-|Kor<4Q{G#Hw6u+;ySMjHcSv)G<=V-;5idQK% zDt0U0qn60RPl?7GP8mBFRS~%6|;DOd7onx&ry6v z@q3CdD*jrr3~h$#sugD{o~F1|@lwV0id~8~Dc-60KE;nIepd0Tiq9&(sQ4SjEIvle z$7IDC#WyIfP`pC1RqpOsF4>L%% zNV%fu$bo(w>d83}9XY<(Fe9QiDAI9)y0T)SA9Q{XElJB3OVY-au5@$Sb4(f-{FXK6 z3WBc)U&0PdX=JWXL>*x)tQfHR&J04Bw_Y1l}@by*HoLKmTaO!)8gbXvwiBA4FEm@;`Bkj9~!|57a}qZ~DlUO=*bPTTN&Yw5GuDhp2om9BfRHRwQwS1ZPa#m3{X;Wb6d2NErPQ=;&d={(K&SaGL zkOYGN!6i?UubKGp8?P-&-27vFGs)&-JpDqGyr66dg?{GJO>e}LKJ<4nVy^>VvL~f4 znJf@;@)u7c3|eX@`E$7%`Sy;2yW2h{Fo$`3=&vx*;I!#t!dTC${eu++CHZ5wbp09!1r#A`o_`PQS*t;9O(lPTCR3!TrGnT*7G4o6>e;iL3 zz1v{7G^DW@^zyW*v9RrM`}sT7%U>nJj9%%O`AX1RY(x-`R$lij0>s=60*lQ7v?LF~ z4ieoK^c3QqU=A-HhEH*uV(^a;@3XY1F?vMT54(UW6pD))_^lm}i{)Uwv#hvpZ1Q*r zL)CK}K`nM4IuFN~na?sY{@s~>ww%NqiZL?lh=zRy$H1&NGpsk66cy&ODZnV$b^F@; z^SwBnYx%Ci{kUeYL2;uZ-wpbc`y$?}_#wqd6u+$aEg~AtcNJe${}ie_=EKooSSQ2q z-Tj@SRNn0%8J6!peZh$LkF00IMU$%&NY?Zn4Bu_+@P2fwdmEMaao)~HkRkWNxg~1$ zk&%v!?bBH4YnlB_>S_R3>3^lG+<-*DO8?$@;vcMZI$Z#!evLl_y+FV6)RO?7x3gA! z!)X8--V_SdKS9v*cFMy~(^bJJAzSA4xL&sj3Vv%01|VK5J#35yG(_fObHa*GGSlg3=9h<}_|X3#z2NP}3@k@KTR*v8SZ zjlpR7Mt)HFmXV)9w;A&bhl6kI!2Qhd-ZV>bnVs&>;Nn|l#==VZ#w^OQ6OmYs{qc>Q zmLryPe@5noGIC;nQ$RByb4T_zL!X?1}ZFHlJ-lBMihQC)) z;v<~z;RyE@Z*FMovnknO_j@cjeL-Db%=)o!J#W?d*c*X6ITNC%NL)d%-gOp-lsDYh zL~Ev4E4inW(J37-X9LI@XX(^fO0J8!!;|0_{w@BDJ7ix4?r=N++~Ib*$_+Sn1`GHb z=ZSw#w6X($=U>oOo@xhRqV+*~vRe$Z;WPjZGlZkL?k ziY*~=@GPv>w&bNK8%H?9QF4Z(-yi(PJd2V1jv4DSm%_vB4Er1|zENf@tduw8S&Sp$ z4L5?o79$2UdY8cN^Irbh*ZboQcY(l2c*93M-fMrnA@`PW-q(!fuar0B*@?^-zj62^ z?vpp10eX3an=yLq#r^Vr!^>YK!i?7}ol{%}dSi_U;?c_Meno(oi^0n+HV4p>l=6n$ zwlEUj@WY6AsCh$<>CIUF%x&oV9}eCSQ@ft=@BZrWhJ4mE{1THloG32$XS|{I*We80 zi{id3h{E=FIQhb>aKEAE6oogyOYl~PqkXb%6W^}!?^7frLVvz9BjpRZNZ1&|Vdo4N zjeH+4gyJ-ZlrJoj)$fZ*!~zxDh0FB|L(d$JF(z>=nZe*+aG~B{iaEu1z$fJFzj2B; zGfF1K2LevG&%F^=XrTEvp^<;3 zD!PjfW^@8}iF|39Q_)RMEL>e4zL_|F8voI3$E5!iwz0v}_>bhQ-@_zsALVutS*wmk z>!>ZPjoy!{Zuu7MYySt|@xk))@4YmIGi|Ray5TU%anQ>nju|U2zwti*dZU-W{qe8Qf!?u31T<*nb-yA&%&+i;8VUc(z3wC7 zUoS$uk?^luP$?skiQkUG4^_{We?zaRcxM0c2lFSUidrh&@>!0fK*2<8ckvz5VxB2! zspew(tUSxa$G`il!@rK#n;HK)luX>do@8L%rY{F+eR`<$JREyKxRAyQS58js~V{-wprE_Q3DB^!l4& zzjrV5fVT2d5De^j8TxX{@&mh5@JrW1j}GGgH|S?;-?KMei}-oa290#>tzyr@o~0dh zxQ-V2-yN*qvG@C+Go;QP)vc3Cj-5=Cp@!_)`06rGYYqIVYsXWKlPz9ZJH}`1$L@=< z&lS)9R;a=nsMwJcD2orItUL)1ZG56)r^>0=3HtEHqGiE2PXA@GyT9qokYr{Wm5keaD>^iM*)9_ zt0J0KzNuwv5KZrf3kz_w|0u|CC`HIV>AA8&CjAz;$J8W4A$^e{!I%hM4(|w z!gaZJ!c>WHMNTF+rczx?cxi4HVQLHiFRyq%qfOsL$C`0C-f{+21%zwIU1!$I$Mu+X z^|+=EM49<|rm07ERw8a?Ce1sK7kUh-;yvXLG3sH@q)9WAmaoDt#E62A^8XO+d&id6 zri}-;>KE%AwC8Xm4X1;`w6}|NF(qv%GTBUEyuft*KkmJ2EROCm$!3g1=x3YU(RM%@4hX z9Ho$onFC*yyN|&@;?4FwXi!4W>)Bc*-f$y_>wCJo`nm)6A4isn^-i2h=`9uM5#d|A z{7ZNon|B=LkV_t6P4Za5xp!mlv(+oJfX%f>=HUtGiDt$jzKwgArg5L ze1GF|l#XLIAfqoJ+>FsH9mm{(PQ&Vj$oeBT;<7Zb2CERjvYD}Zz6KutIN@H<8_784 zMi3Z^uP3kO(|f?nAFOfyZiC&=-+MtX4?i;&wjCZmy@$R0?Qb0OSb1JQ~M%Vy?q zjAM{3&-iy|{!uv39E!EQeAYBCi;vv-zjEM z{mhrJe~8b%WrX7%Oiy)f9qIi2Y4aB?80or>W_E69)^!4I&2O!YdoZR5I4-H*!}Y&m z{lIRtC1{9pJ6M4Rx3VaIO?*{Fi?Gtpl#Bp!Cu!V|-eb@0Tn<4i(%#5RdxhYQ27;}*Khb=YwV zQs}UQVu``Sjw!s)3~6^5cF3M;b;ZMu^BKhrJAMV)+#ed|VF@abkGU%6S+!%$>jCFKaHETay}h;k+934YINduYS?Efpa}(SVzk% zEA9$!iFTywE2|inhW@nk+u~Z*1*p_ZV)i^Ai{5a2R?{km)zQI(f zUAA?fmz;tRPZoZYaG~MHyy+};8y~h4Zr?C2%_+E;ezN1ZjV?AKN{`O_)eaD|CNr`z#T|$@k}<_Yi1%3|4zw%~UEl2j z{~3kK!;@nQmWTC1WLZ%avH2w)!b!0!q33yt)$T*b;TTi!S<-C8g-=`2{=L#%z8Yf; z|GUY0o2q$XzBuNHKaCh<#NGPLhCoIcacAz{i4l)C#w9v(rnB5|&rz&Zl<@}q&r|m* zMHz3v|0;DiC|;{5;|+x0tZo@^!2O`QKd1OL#b=0UQ0ya!KT!V{6}i`#`4c`5;XDU} z@usQ&JVjAbig=$?I()BuT_92}w81(JXP-5cE)XcGgObLM{*jJP7St^q@t(vgz8q-Q z%tL?`ibw0pu1A*7{qf}B?iV>Wp=>4`z<32?k`%@u%k%Giz9M+{iyVt^+!8FI|7|ah z)$vJK3%~rpZjNDW%))TWRCs2=Bg;)TBR9q0%q4^6Bm0tHq>2|NrHjQGnM9USW>_&7C%>-NhF&wHH8=<7J8QMZeJ@M?cZ`ys)!I-su6slj3%ugS( z`RT=YzW%c&KSWc?OLBcRzaQ-D&<(~s$t-o-_jf1UzF}ONgW)ANIui14^p?V9b_Q^+ z(Y0oY7Kfb!C;0<2Q{cD~4whqoyyT6b_ZCY8w0o8El6|1ZIwL(IyIo=cX7 zw5W02^WZ&%H-cUj;*cIbAu+vDUh-7D(0PQLv3r&Bl6QFdt3YJSUnwuSY9I7UdC7ad z{OymIYzMt}S>}MuAFulr0b>3K@z@5;K(^HZ?XD8d@3}+X~H4@*F$b7I*ai+m_)Q-|X!A~GiY z7RD58?<3LOcrxRGN8r2~`MrgT7pi}Y;wJUyyXo_L`{M8VTA{Mpn7e#__;ok6XN-9M zc_VL%*SSu>@ps+2?j~4|H8Zu0<797B71l ze-f&zDp~e2L6^PAX(CdSj(cntyZZ}&0*r3LAC|22@sWR}UYm#-rrnSXvWqhZ<0F2P6TKb`%)=Zfc_Wf0`% zpLr5VcpV~z`S2nD4NGFnUZzNd%X5#=Z^~T8zBI>~@o>rt{;#e06ho$6LdP01%|Rtr zxtu(kzV)1*=9-%EnKX%4NfHK4AX)iCjMHGxq}Ry5^uu34j41dB|6fxpTjbr`--Pu1 zQ!bfX|M>T7Qa4=amaj;E;;&zsj6#`VJuk1Ux+}ajx+S$EExZ!C5|}rmE?5cD8;DQB zkEg}WKflE>d^xVGOZ*RfGCG-Ij^}gPf)j7L_PwITWm#u$2U95W8Df?EL9BH-xM^&C zwz|0G@Q)U7Fa;LF%kc;E8hhil@(qoRJuTP!i|N}sTf2NW7W=gL?$(|LC?K5I)6%ZX z?YsLMJK+VAiC@Wz{8wjvKF0%S$ntFk$lQkCX00Vp(|BOR#N4N7KpHgs=7P0~=k4|t z?^MLGG_VGYN4c>X3nQOzc7}0onQxvMvz`eLeD`AWdwd{Ygp1$w3@(;~`68chX4C+J>0JZ6rGYiL7Zt>^nX&r23LZYamq8CKj^vO2mOt|O zKD{SFkNt)j3)=(_pWg4i{J|R2+YY;>A&uuiFHega3wtZvKD{)UEXyCi7?!_x!)|Fv z;`gBE_m>tnpz&%M31$)q@JJvtwDS6xqWNEhhqc%oUKqcXxK+0WZAZKl%;CiYyBN1U z2H%f(U#3NkWr67WaTo9`3eU0TaN`C{nt{(vjL}PcoXz(ie%#>ag!xBFJv01z{qmw` zSNw!X&wsLXvxOukC0gNqH%A?bNl8oa-K^!rq);Q|$!VH#xjAR;tTE4QUr4gH2cmm@ zI^(19u?-OyDY8$X`#eRy({ytZkl3i$rpQJ^e_^SBH>;a{4*efg{G8&~6^C5Mg7`nv z@ZTxQIu`iL@m8pazo$PQDf@~Mt!II5KRL`q#}vcuN3C02H{x~t%iW`o#}mh5#&{CX zna$s`w+dJlz>f9xDXiV+`g``7W~wmmI2CbDeNy9NZu!h51AFpSAKaT~+NwLTAK*?| zJ70x);wl|eysQLB>q@)`gXfBqNnfs|*JbtT;R1>+fyl>9VtxYJK4X2^F?7v#t{cnFrE3m*snm@7!m_97e4TT? zFD$EJj!tpTfiTn1fkRoH>wz%S4cA=f+8LJNi3ia<=X^NiYZ*i}%t30#BVpNdbk@4i zU18aG>74JJFN8t)7~F@hEG@Yp9%!%NM{NM=T3+;V0k9PiCY#lE_l*O1TJjo49;(93@*k>?sL0)fLntL`#W*92j}*0 z2rg^s4&K<*7hKYHeXty-m?3_D0}f(BnjW#BNJ!hX5s36{?Leew>m-e>M9}HSVM?Th zL*W`*df*3|U9^F&d69l>zYKY}7;ShBj-0vzr%|oh*dLtV(-vHYL&g!tGng1Ih&a8p zAYP}l1&rS#p12p%Nj#+O?-3jH9n zXT6DW&P5BU$2COwRgBVskJ7A_j%I#Vcd%+%Va>{m&tuY2c$aCd!&BVoq zdMYk{hsc{O$F&Mq6|Ob7Y&^&M-GXZyt~+q?`|>_q1GpZ*wG-FFxE{gvD6YqGeHqs` za6OCb1za!V`VB7jDs1wzadC8B%M1RCaV^KS3fFM=XMeK_R|Vqw<6FATuxyjnLHi0^ z%PX_>m6PuZcc6n?2Dj_y{solyN*8zxSbSa?o3b5R_C^U+pN3}hCUsV)c9(RD={5_n z0SDlrTkPAIC{O1Qb~Da1W25lyA_s@L)6G2J;L1X4M_=Gba3n=Jl*?L@k~yIC4GM5pov? z*Q7mvUY#2Y_lTE2E*{O`859qGJG*>KP$lxg1lHm58*v(L8jh)bj(gkqAXoxFe$DZU z#C^^zbsM+;0NlP|T$)pGWpVlOUSWV5yKgPrmWDy3_s_Jbv9P6Zo1H=2|G~H0j9JNN z`{j5N<(P~>mVNfvN z5$^UjIu(`sBs}@OIu;kp!E&*zKE0PKgW%opGh_73 zZRq_~5_t5)hu#1O1CKbncj6|FI342CCh zLODx~X`M+Iokz#oV*fSUf;Ihm&&_^;{D(88wfA~w3@8wx=qW*Ub0A{KpVJ&wU0K)O#}W(fA7P{5W- zp3s_b6w273s`AKQ1e{4kgRBQm!G~bI`Zp_XRAgDBtm{XIofA1)*V+N+FddNJ9^!?H zS1PtD_9<>x10AIqy8CAXO$g;W7zD7HtLnoVfY_aaKVd>ngc?n0QHnUi}1F!MU;uV)06 zI95J$ild*-J;NY%Wpf+TiK`-ao)=bCS=bU}PCXA)qZyNe@#$T-SC(D=H(`+yfz#au zcN0?0%*@YiLjE!f+|B#`;~4V{=bDmxm|*&Oqdxr!B(nsGZ8aivK&`yTw=hf9f12*;=CHSVAAN7+R-(~=v*47JjS_u$FM znGJdmkYR&LRaO=nTZ7EQACQ|@{03VXH3c6!*q|a3ZfbL z+&zr>sV;HN%$>+PeCD0vnw=ZZbf5i*xK6Ds^fv~XC)^!s;Xr*<3dd2+#gXZ> z$k)O?i7CdXFU0>*tby=pWWdoCo6_xPIO`<2FH-$EJhCif+;5;oe%*Ityu-5aU@vq| zPgnmXi?=8uRWz6Ct?!8TtI^5lMS?^(;nS8M!WKNp&kDU#O9n zS2r>AA`M+peNhPKs^~FEyQ=z!jQmE8TwncL7NI`kGr9IY^fRFh^lvx*w*MNL{H6lE>rz=hTjyOiTX*+*p;cyGyJX5KTG(_ z89V~kc7`8`Oe%a9v5)RY+z%mIMW#Hgmaa7$(vQ{sKTJMG9wD?BRdb3qGd6U!p9L}A zXMB1d{*Qhfe;LrO%uElf|G?^+5z>!j9%QMX0ii&STY-;mIXAWI7`={2Gt{Uc;6 zDnI5IScULRW@A+SI#Rnl;|F?Y6Lb3*8M7_RFK{Zx5@v2Pe}gcTUY12&;QYtx1xnW;_s9=NR6s?uWzbolM`N z?nlGwzu@g!)%|!_{WGTDpzbe+L6(K!%P7@$Y>@sasx4cdWd>sJ@*w*<-+fGQ%xbt_ z$y^szf1Wq&is)vwE@XbXqegLV0WSaxJ1!wVHOR6_@)4+eGT)=;VI1y|Q$7R{nS-G2#*YpH>qcpi6AyWD%=Sm01uR+9cH)4b}A+!^SsmFh~ zZVIGrb0v7q)XuKWL0J1=2#>&%j88M>2{*G|PE0Y|VHmsWteMwx1AU3&FvBF?9Fgydq!>ctfoPWW* z`46OSm{(hKEyksICg{6x`SKr*z0B}2L*BEBOB^Zh;hdw83fj;p*n;E{F#41@NbX24 zndT4+{tAWO=Cf8_%IrT@Z%wW*T~ZLp$V)NJQ<=$ypkOo|fKzNQ*WFg|61tUWkJWTtaUb$WX-3zkLVE+GqJ@@@ zmYxkQolRSVLeuqqC<>x6JB3wmTy{}m^?A$IuD)#L<>wXFz`w1lu@~7rf90z4SZ=w4 z-2f6Z5&pFF^dL%KN1>w)q+E*54xGc=)l{@xv%syH?QP6LYbU89iO9TIKd6|Jm7a*y z(6a&Y+B&-Xds+(JkaiRrdZCTLY7+-JOAVveunZq$NwKZB8F!FHqa6S2qnQtLkTCBd z!m)X*bI5<%`f8EuHpsOKvQiS6llBYG?yg>FAvDO)6#&A2B`MVF2qHHYxJrvj?2;)Ine4{Q|am~utWu^UKUkbVsUJ= z4QUlkU0s{nQ0_)Z+0bIzyG(sTVM9YlM}ss}*aDou)Zg3ET)>h}Fxog34 zfo>;S1!cSxd>MF?rPvV2x22#K#pB7#C=`zF@`pu!N`=V8`M5a!ccj4v(AMS4V| z*9g0%ffeKbq1PH;gCL*Y640}H#;jaSuNQWo9_4F%MrJIm3m!hbdM|(EEi8Yx!ER|t z;{wpj)1t=0w!`hyyVA>FCBlr}dthf8K0lfbp!Wp^s$m$f`xVtYn0?gH^2vt~0d1w* z;;l!#6U+f5Et*pK3T+mL)Jva#D@R;?@tP)vPYoo9R&w|O~tf3jB zXKq8^|8U4B+13(638Y(RTyvXI8j{gkM%gC#4X=0upJQm zLHs9QXUg+v>1xHN2a{kvM-=3d-u4qlc&2<;hn2md%}r%I;2gy|#j_OIZ!nziF>#&Z zM#UY9?@;8VFXQc0{FLGs6~CqUeML@wG5${#vv?GAbHycbrXnXy>26f)R=i2^4#oE> z?o@nC5yNIl{~g7>ia%52UT4zhq#^M*#d8!dP+Y6X`6tG^PVvKvA5;8{;x`q4s91rQ zMd&LoR9vpOT5-K%r{ax@k1Bpq@!u3*R{U>8sC{7f*@lM6}DSk}xvx;9;d{*&A#os7q(SVun$%-|KZ%|yJc!grC z;;oABRQ!PA#}%Je{GsB@ioa7F%gH3%_gKZ*ii;G_S6r*utk|o#UGW~p4=a8`@k@%| zRotVPMhC_F)+sJiyixIX#l4CzEB;O~g^rl%Cn(NRtR#WKqDuDN(cu^$-nNz*f#-u3<$xQ?GXD?{jY zIeW>G|Cx5PUC-9hcZo}hMztlJNR?mZ~tSn7E(9hKNOyqCK3gW6lFe`=T-PVd)zPGSA4;h{u%@J8#GxBAgQ7rSkoHvMNo>Z|N70EoKSmyb0-aM9hp2l>s%#+8WN3qQF=X`Fl z%#*U6D3*CnW>}HTGb)mKMny8uD3*DS<^5urCp2XRQIX6uie;V)c)wWYDU4k#^SqgN zk7b_1R3$RcHWp%dnde;4e{IP;@#R7n6g^#i5{sV5Jike&Nj1~ePx{VmwQSR}(2D9$ zvh<0}vzAXgk$L_V(^ zCNj?xnKqGm?qJ$P=J{2IdNNO@^<__JLPDWRmX9Sri z7Y9U%%#-5wD3N*o4Pz%VPkdg{O~o?L-K>B_=6Nnd6PYJv$5A5l+{tVwGEdHSM2XDv zYjh?u&t1%hC-eLeX?Zfw4u*R&&yO%Cp3L)eB%+b?Woe+-x3nL!mTo8`liHLn2y+Kvk znYkUHSU!P*PexpY>S2}kp3R*tVxw5$%K%{(|>&h^c9-z z9^Gc4yY_^sOx!H7FFRp3BQ#&gq^sRc+L)F%xrOoC#f87|X-a#?r&+1fe~d~BPmrK6 z_+W@C2xNeCXav1(u6}ePs1ZndM17K1v*8tB}jHC2{vga z#bXpDB~sW?hz?_sWQiE4RD6}_&M6j@#qwzpJ(b9Hiv?8_c~Lah%|~=Y7dF}^L^;^< z74memOHm<|6BjbINHU$Ayh!p0`L&ZQs$4Rb*@6n>TDTo*&B?V?P~ z0x*-I50Z%&ixw$`hCEY*u)G~*&~Yh|11G^W!C{HC(+HE%t@;FSj=6!fbacNG$ z#rXKriy2Tfuy`Drnw?<-xc<)bFzZtIn;q8RQha&&r8Z+>r84Vgl;b`WlI6g(eq4^# zuv;3^_**a$AND-VN?Dg*FTaN%mE|HmBI}RXh|AKz8mz(8@p4+!SUs1@tkZZvdBh<- z7RTt7%B=4LJ+5OkWA`eRSK6xpS46DJ>J_f zXD;y5Xw3ES98PAPMSEE`1U=77tfX(|aLBCvXTqB0v)yPJkeZGis7LR`fcK%0S&vup zWO^yHHeQoc@V`ke~2JzcP{CD6O4ygmmxtD<4 z6bR8-9}(IE-}58#?jU#!jg)f0Bl2!n4@czPt{(o><=r#US+d=qs(7a2TE!;C>lELr zc#q--i1>JXNbxcC|F+^!6#q-Z|68#V-7fEYyy8N|OBMf&?<44bGA=AMdW?kgCiu59jakic9OxlNu+WbUbv$lRYps+p^^r;}bAOp(vCRD)q*5$%pM%?E!_>0eZ04m% z=AKGq?kOfrWbRGO+b}Zszr>yI4pVDg&BikKIn3S_F0<*}Uz1QQb7x*s2PAXna)WHD zB{zvFW10JO7Nu0?eiLt;$lSS+_v)0$+*7{HJ+)Kw#(l=ASmu5;ZxGAe&tr8I$=p-1 z%>9$Rc`S2Z#B{OD{V6_;SmwTku2|;IWq+wy=Kd?DE0VdVie&DoBAI(Cmbq6lT`Y5d z3-ehdb5F%GckUHW#WHtpd`rbL_v4wfSmwTgu0-bkJe(ttx!(!;uPvE-J#rMJQ%_gl z%AzMS_cyVYOsbx)zQ}iGs~=^}SZGD{Or}j_?i}Q%5}7-t`Kd(aK97+Tnfv{$$VBFT z4nq@}`x=HOGIuU_O(in-&+|r!%>7|TPGs)CXXHfYegmC}%)OD36PY{T%#-9 zWbQAqx)Pat2h$}o_X=iuKQebJ$1s_D3NrV_jGxHd|BJ*EnLF41rV^R^dl)&9xpSli z9TwA&vuMVO>c3@ZB6B~QX%m_IF{GTx+{qQD5}EsV=uBkp>q*L!xt~Q^p3MDNhI=yi zzhX{2nfsSW&Xc*no#CF${U(NcGWR>_-k;3<1BftEnR^N{_djEX6Pf#WdH(~IxudBb z%XbxzNo4NZS?E_w=KgCG5Hj~(62z|xW z*6tuYLn2>3wFP4CX^gXGYHJXlwFo&5*G{EKDm+`pBk>mFY%54|qx1>e5g&W!PYgN9 z6J8|u;(f?wh3oR1#!AIok!2N4a1Hzp(N<*6^LLF>{G<}TU#@?c0*0g7wmYIU~}Us zgJjV&nN$s*A+X2GW>&> zj4ef75``0QE;910#&~i%eMz*XgVf90?f54mk>0l)+N-^Tmqz=$ZHAFYk3#;e;$yT! z*)BVruo)#g2r=`a6cJOVXB)5G_V7}X@JM9A$(Ha#BLYsgsvl~>Z@gJ~WFp`FOL|8q z@0aeZ#wLa!@pqe(MmPsH;hYDDhJ+IJTmi^ zhUpmZ{ho(emzKo47~eg9Ai3HzJ5<)=Nt0 z4Pdh*>w)x$M(-NfnPvelnr6%gGt3O_48Km%{5K-r3UdH0O$Y3LIdGD{SOhCHwVz@y&HC)-ZP+A1wZDGGDEAE zkHF3}e9kog1A5$`V1{A5_}q;)!#;J?(DLHXAPj6-H{;O!9Pv(YR{1v?rF)#mVV?V| zL&Rfz$3nTlZtPvY8=gnQ$*FN>mE~etg&hQnLA$!x>_EG(=OtF(|8U5Q{rgiM%(jx4 zP-OWVV-L3nn!KkoE_{nQOG=e0p*H-vN_+~5;W!8}lYOqSLR>-{8i0O>B3 zFP{NF-dlFMAT_n4KnXh_e)A{aIHx^!Q*QT2D&{P6dH2=9fL4Goa1U`V{=J zADx26&UbeT8Vl&cIME5#Hxi8#1)cp>{_bTpGls(vp_eFJoOY!@Pdli4Gm_@_oeU4V-6c639 z2OHR^Zca+kU-n=FWvdQQ_Fw~dYPiUx;pR*U?7;@g z9&DiO!3N46Y@qDH2Ff06pzOg0${uW>?7;@g9&DiO!3N46Y@qDH2Ff06pzOg0${uW> z?7;@g9&BJ14VdMYJ=j3mgAJ5D*uWJUE_<-ymOa=&*@F#~J=j3mgAJ5D*g)BX4HTI) zQ1)O0We+w`_Fw~L4>nNtU;||jHc<9p17#03Q1)O0We+w`_Fw~L4>nNtU;||jHc<9p z17#03Q1)O0We+w`_Fw~L4>nNtU;||jHc<9p17#03@b`H4Sx@EoQ1HFZDQ+afzgOLx z6>n01ii_g@0N+OG50L(BzK3uxA;K;F2>uK~Qv6*P7K?_5)f;`r{P_zg86Jsj_)2^W zVTxtL=gs}`B*=wp2j0K8ZvFfB21^EZhxOQdy$v$nZpw#)mqhOS@GvzGN4_Dyal`N+?&QZZ~vq^ud8bRyt)i|KpsQn@u&##s<2m_IY}MO-r`SQ@Mk;gC3e@28s6n z3{lX{utne=;8UDK&e#a$qpjQNkH&{kx6@M$1kwKUhz`;Iml0s1{e?(vqJ35$$s? zKeyALz))YbZ(HWmIoUxQi}pW{5?WYAjzVs8jeI-(eB6}V>A#0+O19IFXBJ}7KA&q8 zi}ruYuvoPJ3eqRr>HjZXv1orcil1z!zl^R}wEtaFDHiQd!);_cJr=tL``k`Hp9vGu z{+pS%VMP1CMXqE!z0_)2|`PShU~E>L?QJC)?>i%6g1N`!!4#i}q(T53y+fTYPS@X#WQci$(hv zGOS3nKeFxgWxQW3+W!jkStQy|w$qm}EEes*jp<_1emCzPi}t@nS0dV9$&3!aojwHp z*OqAi31nQh(+eY>i1u$|O_=aLEgHpRXSR9_OK+hS)$d~I6Vd+b__Pzz{u+`>MEhK< z6D6Ykkj_N3|1mlf(f(iaMu}*DBcDhj+JA&MN<{lIR!KzrmojZ4+TY5wiD>`x3{6D) zd^7#+^czUp6YUSM5T0nC%TxUA^i${_EZV=8#qmV@Zvz?GPA?rmBHF*6w@*a-%}ll* z(f)MgPPWreX0{U1{#QvR5$&JJ(EW(^-@@qLcKTMvPel7O=}biX?_=ylwEu4mO+@?0 zGBgqG%lIr2?QddeBHG`<@+G2u8T2Qj{jam`6VX1`5cu2axrWQ%PJaQzJ<+}lsXfvD z<0R*a_Wz8J$`kE(Gu#vHzm@L&iT3YCgprE&xt;#U%y1&wmjx9ED%$6EdcLc+ot_Hn zU$yP@ub_Zo+40nQ`AJ%P!f61S0bC}|-z4JvHB);~pW&uO$Pgs=4-qys^Z&}Jrz6Aa zxMJ#OB!q;*tET>gICz8b@)Ksl?+#oiLdp0RiLe@zqiNdmpJSBYGtpfkZ!rLC6>o?& z1wr@@$->$xy|79KE}bU1d8aroKe4;5ISB6-lGg*p zD=)fe?xi8#$GS-rEi%$v2v&1q8B$Z&RPzOMB=J=F{Vmsd_% zURirr`1a`b)NScoGq;rODCannW&jt7EU&Cu!Si=-jc!TpK!dVyw%aEqd{VMT;_Avg z=SpeV;${P}um3J)mwBOKo8owUDK1nN%$;a;U3kNi8D73`1)Kn_6TW<9)D^H{yJ-|j z%iK6B$eJ3qSTp{jiju_?uicIPo4a~8;e=k~w@7dM(EI!MznVg4%$w zzt0W)!!Fv;7mFuLE=ZQE$gcPOY~R7=O0;|;Z|@oKqbM3mfm2Va~_(-8QeR|?-=LwX(;pa zzclv!DP5cKEQE#kAueU@OwV`_KLoSXEldLJ%e}7_oD!FK3NFSUE?zC-kiHqxd&=`L z>rzB8JL7WYHNU`SEUa{GMmGfapGI7kgLPo#0Q*ry?=P{(yx)j;VWn#`X3uctB0ZMR z=v|{}7656gsxTwxtn$xzBwxBVqYm-jR}zoT4qRX-VpEIz*2Bg7V%|Q!@6E7#*$$oo zz4PH`#=>rbhfi;tR}P4PVtS=(GmeJvJa5E+M(^FQ`}A%HJ=|LIx4*R+3qdc+Bm_y1 z*CUD+vKaF2#pdwBO4nv|BOV@cY%G72`KMxEoTZL4a8KY-zL*M_8u{OhAkpW~VLQS(?5);ZAfu#Mp@PRmj3(v-daN*>k zopIrux;HoJlZQ4p`RZSKZibVWcE+Wzh#x>4Ai=N3S`a60ba&@-%{#07)6IU%4Be;U z8oZH@3vk((g5m5}%rN{-<^a;&RN}rCZX0tjeNRa^-&-4dFkD9w7}F`c zJyR6-JH1a_T+-X!11U-?t+w98@kjK5!X6CkX-gPad}riPCU=Aq!VsyY^ZIz`;`7kS z5Qcr*6uclyi1>J{Ct_H`w!}3mpQalxCfg3(GNBGU8}2E2$n+JjBu>LPl!*8b6Q}DM z4#elTfa$-X_zG|;K5A@J5i*fQ+nI`Yk!^@>_VMgrWLj)rYhlm`kR*tJa};GQ4BWC7 z2DnuHWi1Tc>(sqbafjkN6yK}3Q}I)ZUsRN}Frf2&b?;UDsiM>q!euQCa3;$Kl(jHG zSqlT~R)1Lw1NR;3mWeyKcdA>~!oV$JHQ;yDU)I9FEo)(bvK9s?Yhi%076vG5VSut0 z1}JM`fK9At;B|^0R+P0c@c)duWi1TcvK9s?^UFY43j>t3FhE%g1C+He!1d^4Nnh5& z0A(!0A(!0A(!0A(!0A(!i>-5@6~?{KCry+B*l{z7Z7nD(bE82r~Vt&zg_XI z>Mx>7#N+ppdS*D+#PsBW=ssKBSE~DJb#GDkjq1Kz-S?_n`a}E~@*70DeO=JW*VYy< z>KRVo!-Cp{wRtha>wBnkj8eR&ry5^Q2nL?#`kBFNIrgQ|KUJ*JKb@SF5UgS$aH092 zTXT#!k)3sj1`$LnXu}nRCxx`rFd$r)!ffa)39b*)ZoPqgbfZA3^w4NxZKAHxq>^O< zHl9;GDXhU0z+`CYJg3byT4u?Ax?V^Li_fl{i z&W6kPm8K4t|5Wk062sQOc zx8Y*zG-F|B!UJ7oZ0-dw#A@P) zy9Rbk1FP_vd0Nz1{appOPwxc~Vc$r4^k+QM@51HN+Xx5P)Yw?qCiwXDe(B}UAFppm zJWE3&x5AO9MU90~km}R>y_Y|Kyv}hr)6{DE2jSSJb^^nAy9gccRTB_ZN;pHW}6a2d~+<#(p^T{V>_`M9QiNvHZTCY?m z+oBu)>4#gi+gTq!!f_8iFf#4|o(y^sZ~+n3vQV*2kY3zZNgbA@8{sP7fWPcrOi?bnC?k1850S{)u_>ct5Ntbs(gK9b5t7BK z)sOw4%6`$@isZU~pOgoL{swu*zAf zpQlBQg^{oF=~cjC^+Ggy>|`trNpOBGPm3C>=WF2h=}m%z-vH(hL)e&JFYG=&%9{8r z%~%-gz^6yOik3h23zomzV7D}+Q3HB;TGUwBcDQ|dr+WG0H`M682X?06^P_nq=-uP2 z^3O0{_bZAg{+>s?q4=eT5QetWZSgq2H4=VlG2(sJ5&?Nn8X|5L8_u)j;Yqm<>zm~u zvaBExn;ob;e$&kuJ#!oS{=@T2{vCKfYYmRtVE%|LH8CkXQNNF8>Bb|NJcToUZb+Xk zDKUpaxA0L)j(rFDC-xU+SdZ~1#@oE@5MVY`OileOTqkuc!0hNxm1y zN%`<6U#4k5rA@CP=b8L3@#u=-xUHlNcE=-3*B@{4X|z1oPJDat`(Mvf zc{kjjeZ@+7uB^w?uzrjOX@uRx$<8A*e6*2N_nn%_%7v{ zQ8kwL?QoM1F*DoC-~M>6OF-{kmIz3CyzW;7i1~NK8wt-vWrstRw{(t*OX^3$bFCjC z&$SH?mE#ICRxjo@^!*P9&s99@ZJ*OJ%hTo)g$k@@(-TjSRq7E@)A~<|imgvg-2)_|Fdalz3z=l9$qwXVfT|w}9 zS0(aq-_=sS>&RS}UBA| z`7bNi5vJ?#GF?aJx{l0su`4_>*AZd(b<+N&$Xhg;XRsBXcC*L7{1DD_t)U!;z}kh%eb)lMfP^cwO!KTIi) zjo?RnDnGFM=-PpaLG9bBF;<2@^any0A$16H4VdUYM4aD1pP<=)1^Y<#4-Q_No~$`f z9gSsQXXeh5rC?l(u4}+D>Yw4-u(D&qaAGdUXjr;6&P$OE5yB{Azs^ zs`XyTG07b5-2`&&SMyvz)*tTb*3|`6V1o&M7`J$ChuDLF(uVL!@OgI7-yaZ=DWArO zpMp2E0dlh#v;kg@`TR%^`g;PYOdB8$`a6TM^XKBf{DTbHhc-Z$D!qfZ%#M8w(X%+{ z?{AoRDsLFg2y$1@HGPe^(n0DG+*A+xTTjZfW+S8`o#Lds9`tuFqm+v_z|X-Y~+1TcO|aK zQ889>&>webB?tX+A6jzI-)|UphD$dk$Gv+c2mO5qw~>SXZeUn^(BD$tAvx%8Dl?O4 z1H6M&htURzC57&wKdISR8(<9y$Jzkb@usmhK<4GZ4*DzLnew2&6f+cS1N;E*vB4EO z4P8rb=ac7pYVV-GzeAh@IOy+PBpGW1oIqMd+5nS-{@%cwecauAa_)DG8EXT4H(jwd zz>hL!tPOB1?+|MP9K!+?X#*VDL4Ws=N~{g=x4c7&A2j1x6(4aHoz*B;I*X<@c*F1a?sy>EMB4wkSeRIWG)(FRz>$cZ+< z8yGp!26zcW6K#N(@tGvr0RMxL6K#OMU=b2+fPclvi8esKnf^h4+@b3q^miYLdD;NK z#&GYTzdXYSYXh9jyLj3F*;&g$f6@UY+5jgoUZM?fIi35_2FSHeLml+TeH7k7e;;C| z_M;7O7Ybq80C~{gOjdrP4e)zRo@fK4W-0HWKhDs22mMtuZK4hEO{~vE8{pH7oM;0i zE9D*ZCyHznZGd%5n`i^1usY=(^mhl{-a&u!m@iKoU^{c-X#+fw;hr|Y$C=*K26!`X z=V=4{9@Fnn8{mVewvlQBU%(hOTpb*L4HMVU z{~AfmggabfqdhgF6>Gb@6Rs#|;QwJ*!ZwPN^hP&}riO?| zsM^~a=gyxuzYZ#@)L8?srW$WKoeg+Iwi|21Lkl(4d1xrH>O9z!1N@K@`#s&Ue(^TX zZ{T>vxvOxJ--5a4pPt%sb8Tvz(}jl)GL+*N{WW=<$u+}=3;lETl{m)F{OLzd)AZ8K zg&kRg=|I!?IhqM%xmp6~aA0GL-4bmI{#^nzK{aeaW(tMg=CfAt0 zZqN(?6=V&4(9~7vZ4D9)PzmuuVRIAoR<$>__O{)Cc&L_!_P(}`mV&5%DqP>t-Y?1N zO!RSCUU568d#!-u-MU&^dt2axlfF6|+8u%9)USr-=AdAz#PnRW4MM!5u~&4?^;%|o z>K4bk#h~D#uWM7Gy`dMBTRK{LHnem$Z4C-g^(KlhoSLe^2f}r>bu~hnTdW|gNUI%` zWMHFd3)OWY3bmKDbq1Is%$phr=vRf zgg^*hqUTSljIW6mSh@;CmJXKGB%emEwY`CFg;N6Q?i}|zRAE!-m4hIU?6F#X@w|q< zzMi(m{yw~-BR50IK>SaFeJD!To!pMe$~zj-`QBbP9EP9%fxy5|3kXeihAFymR2;c?UaD4$gJ399(eBWrbz7B=4SgTwnA%9P3U{y`Bl> znQV)FSksAlHTGC>3`%n~F7C0))1ij#V~#n1bT7cgxU+FF{@Fk?3{N;$e^Zt*j5s#0 zYqRV*r^N|V;_pHc6SW+qra(YbPefa|5jYh$*aoCd9lrC!>Iy^U+FmY}gL}E^6wgv* z|3iQF2gG%X8x?mbzC-c7iaQlQrT9g~Zz+CXaj)V}6|;DxypQM#1I|>p=n8{dbcF#$ zR~S%qg#krZ7*KSD0e5OT(G>>wm(?x0!rD-0;Q!hoVH3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q!hoVH3@Ez7 zfTAl5D7wOcqALt2y25~>D-0;Q!hoVH3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q!hoVH z3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q!hoVH3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q z!hoVH3@Ez7fTAl5D7wOcqALt2y260m!^-C*y25~>D-0;Q!hoVH3@Ez7fTAl5D7wOc zqALuT&N@1xD-7;s>K0vLaEq=mpy&z%{z}97(c(J)wBiCH!e6iMa}^8fPab2aeF3NP z{lka2mI!*%f8fuMeHZ<$e=_A-!^ut;oKZJo-CAFUj1HzqcKZL}>@~~!BcGCUb)BGyd%V$F<68q1snv3y@@HL}GjyT&BCG|*`5Y_S}C zDaI6uX+;vni3P`*ptRBirNJ**t*+E~FhRh_K=T${A-_E)DC~nhok_I!w+}A}7s5`% zoP_JLm@qn@USa*1za^}ghX1i>d_f*pu28@^7(Wc@iN5VB{H0EA$A1`(dkMgMRj$Ne zE~xl1Abkw`{V)tT+ZqMT*Pr5?o%fmynsBq}G&jlC(2kik)IvP7_FO8Q_Y0XtLw&;A zpGTwH3?C22F0S$X2DV#p+1m;0(EBWy%uF0PnYcR0i590Vr&zm4@qAD`rE}nG0{c7L zn!1`>lKBvs#Yo5(%_S5$3WfG2r|(4P=!!yxjLu~UwW81?Bk<9Uz=s-t`;E_rOr_)R z8}SWd?=lG&-wVd0dzQLw{JkD--^e=zkJ9n?dOs5E7LVUKvop$iT%VvtjfJs`H9LdY zvGQ#*V_~J^?;R+|Q;5WJ@EdC7SPi?SfmQg;8J>q(OUK`w@9-pNd%U|jEyV=Vh`zgy`=^DhYlKdIH?MTC~m6>)g zfBPGMzZ3Lk84)C=mDl}>05OjuLyOG;v?LF~4ieoK^fKa&Wc-c8Vn;Oou0x^nxY=;y z?=^S`CtD`G{CQqtwS3cYIL6<6mWgruK4!-rer{NgY)OeZlsZn>cMPfH#J*zjaN0aD zi7MY((Qzf?@d)E(;b-yeSgw)iIGMwE;HTMN5Em&fQ@l{|QpGD33yNgem~M;WTNU4- z_#PrY;xcXqKCJ#SZif4F8vaf7|B0d;?uBr^zvSB|Ydk3*{tWq!E8p(cYIfD+Y5o{| zdld8SOa7$!cJ`MC$G3yPi1~KDbED(ie-lpQje-geG>-7?;4R}>H}odx+k8R!Fns&3 z50-CtlYM*~Q*hzUhOv)#GA^WV!mgvM8@%B9AbEJ#OEh`XND3j3@a;iagt^Iiy<0o_ z8XAERybEqL+nR$S%zA`xPsSBT_;!11lJN$8 zj_~cM1p9XReEZ;!*%7|Ir=i>C+DF2-&p-W)1;XIxhd;|=y>c1s<0-Kmk5SRMCkJ*{ zJbCdh43&O$=`=Hi@%{w;GiboR46NqEtMOe-xXKhM1^zhK_{h4apsAB5Y&2|-oa zgz%^^l|4Q?D~nA{G($0h9Jj`1KitS5J;~=5&*OQDUrJAia3@IUxs8L)moPOkaVCbh zkXI<>Dg1I6Pccp7VLnF85=8@h^qeQ*9N2L2$hY`&J4@h(s5~}CZwY+N&Opx5alCHE z!p??=KSxC-iTS|rUbN}69CX*?vNW&;U&FPF7Bv>O3T~esWuAHXksjYSqjxpzmIl_~ zAviehH)CO)@Gv{Wo`fS$iyG#Q=2~3-eYrfF^8~yvN0WA6zW!Fnum-z8kAf*Pb}yoP zJ6#CRq-tulh1ZVd zpN(x;$~?%omy2|RU?VcYxgYZKoa^E{$Mfd+Ea=ZR6o+dYpu4XZ%RHMl#-E+x=88^R zuwcY8-uE#xs4n~F9D`hRFPZz}V9BM=2kQrRNA=kE8VrPJhsPm*F9lWa+FJ%0@AzE< zyQ}3ag!(w<9F zn`o^c_W~nLFVL}uol{VO-6^wQPv3fU;ps&P8J|h>K2?&MK@)ff`9q9zV9%sK3-3&t zM1ybOih_^w{}6FR^6rhY%Sd*7=qv1|vE1vt(RI?wU7Z*%c-N?_Kz@~m>+Y`yir!Nhj-?VcQY-2<zNHwRUFbB+QT(IO%(<>*F*YQ37zZmC{tuGXn1KJ0>?^+7 zbWdQQ=h^?jmg6nJF&4Q#J{uU0@<^4<*hij?a5IKsX-2~`e0Sk82xH>l)M0u*)OHh0ZAk-Ot^v$^Zbtn%SBN^dRB z?ecM*c^YoU7{9CFVApqzRX)CEOnu4qnK(>?3!?4ktMR*6!|^)POhY(oI538%v@#*f zI08S7q3UEyHc=jd8=9ikc>No~?XF$2$NN7)xaUkHph=I2OCOnXT!b_n4}=S^3+qc{ zn;_rFaL&QdNQ%?5oaEHxL!tZezMTW}WA3r($vz$^mSybRnI3ysZ1x4G9jC_h2S)p# zt%e6VZnmQ_Y)A824S$uHw}vjngZCU*&IRYa+M7AwBN!eZeWMgJiiYDpN#)s!XDf1= zALG?1eo~R&a&*5^af9Ml6u+zZLq$Hb_`Sh7fyC|V{wKx%R^*%;rprFYeckxHWB5Tt zbTZyUl%@VS7-ZYgefh;T%@;;rGu?CF6DN-E@jtXddB_Cav|OMR&!hqRJ%jI<48KXv z-s$RhjZ@gR6TS7|1l(Qc?CpNxXE;^AZ%#V=zBv%zH-W=Al{<3_p)V+X^^O4ukME)F zF5=qZ5?SF;X9>qJ9>Vf_iRqbdonO5F{vZB|Z=Ih5e-CuhZ9APU2L<4ppD#mjrSj~rokYj)q&^+Lllpf2PRi$}QTPpXc#?m_DscZLJAM}R z3#aN9gx@?IvWpI&!ozS44jAVipUjRgJ5iz|z=ZmEHp_Wz_#E%nv3yot> z%kgAbAAJj*1xI`f@hupC3pGQ+w~+eXLMbl@mIJ-hB(0p=aqfD9bDsb@xu2nwdn%4z z?s^>Y|KU>u8jLIdWGacs@gxIJ1&4eF1ANYGo z9uZxCEsb!;WoDc)dg0jd3um30xeK=KlvDivGsl$83^zQ+W2wVjT|SeHVawRxO$_-EcFsBV z>{I7BSTnsmjL44~2P=3Rd-f?*gGJz2dJClmVf3)hAMB$|uVVCx6n{|3hgTP$Rm+z) zVAZZ!7ac|B_tih_!>ZH&`>y`S|C8$jJMnNJIz66+q1mNqfPOTG!}9x~yKrL= z#>By?!}K`TFvj>D2M2Rbzh@xu8_5`RdNdq3mNjM_28?{f;q}+yzdyfwOdN*g+}T%o zqQ>~mglwm`2@eF;i|OUTvRx=IfHiS&>XgD`Do@my_TzZWPVW(Qvg>w^!X{<(iX`ZMtew_1Yr?(C16~c|>!(bsw?_Y4v zI2l-)Hl&yRy-NQ(U`^T#my5yAWS*!oeqVu%OVDcEk3+a&MgW>{+21|$5$>Vggk$*d zSsZ%K$Izq@ju^x3@wwZqr#e6CTBpVKI%& zbJ{Vml7h1lXS@rbROM1CLb}jdXQUNNM25KAISZ`1F})wJN`9I{7k~QWBE}aU7rnGh#@b%~~{@x8zd3DO7ixcH4W7PP6zlb0GkNp3w<4wEou|L!B?&EE0 z-kA27ivDs71GC4QU4`R4V&X79!_DT28snEe-sJtW(F6jTaM|O{S5S#9@aK3b2bT3< zy|Txf86>bA?#7t(X2QXKd*2L!@tNM9#+&yd0lqhkG3}Q<-n<>@r4fkfF&|UD?D6I+ zS?QVbU8wN@Vm4dlj5pg5t|#NoHe4(--t4dSV7=JpNFp-a>$5nL;(ld0$*EOU zw8@`21c{66nWoJmVfC$-uskKQk<=LwxWyz;9sM1lOpdS#*=^R zz{@G)Qe3IXAq@R*Qu!9e+tq!c$}%2A6Pxj13Ed%=s=pZr8f3p>KFcqK(!yT+J3ng5 z#2$|alLL?mE)U0~EXjemL#fYxQ0A`o06OxaMWdbbcx*lR47ta#qW$N2zb^ZO%|G%( zb~kVyq7#Z@#r1vAC2-YY!XM@ACN#&ux~t%0hRz@jFjyWA=YX?yptycJ&Y`$|9iBuv zs<{3o`sC#+_-l>S=r%!;C_ZocsE)>^?v4X!Nv{78|7w2=KuOBI{{{p2EbC48% zhhub0v6%OOFDivcvRgxO{a@kk4Wx$R;LlPPQCxoyPK&%z`9G!a5ywNqANB(1==4Sp z4^zxvMlY2T`A;#3O3D0rmL!Vny)h?A8iSxPeH#);@Mrf+IO%g04tVEmgZw;fi8ns~ zRXBJPhVuvo{4X${iJ=ta=OK(&9!iP)Yw?`&jtr$_{&(5-u32&EgLDWpTm^S4T})u-^zp{#q}(UcRm})FJ6b`#laM$k+>BHsv4IDJQN&5 z|A0>Yp2cA}QcHLZ`7UEdit7(%;TI0)5gkGm*ME~4w@id%zXQ<>M?;+NeWjqP)$#6Q zQgYdsI^IT&S5S4a@gl|bR4DF6 zitDQ>MT+b1V)-J)^*b1^gW`JcdtnZlf+}dm^d1Y#JQ)%S{dic;srm8?AyQml%W`&5 zT<<*-rZPSMDdzWl7;kpI{5psf*He|b7b&ivL+Rxwq&cCuz8@oYuekm(1pAmNu1ATS zzR!KI6uU>(680SaJOe zY{*z~{UZ9titA6HZ>+dpe)7bM>p3R$V#W18XW&?I{qI=FSaJPAim~GQGZ;8lT+chx zTj8w+_{Gn8DgLwYR(ibI{Nfk9)ETUVrMRBIF+59g{Q;CaE3PkPbu7j8laWl)%X3rm zXpI%u7cl!+aeW@6?Ll!ppWX%D2sgzMv{&qI#;64MYU+F@Q=(#S>d*AugW~!WgXbOe z7Yv)XdQeDGbjXFIHUt1B$WY`jePcwMU&we(`EIRm!w1#r6Ng#4N@2cuG2+ zrMUjPOwLkVe=+?n#q}%cZz-<7n)03$*WZc&Jyl%qL25{vKg#@jtGFIrb?~QH z(I)RFxY&c6*eD;8;`-qKp@MGiA?{ts#620s#haN<7YsR;WselgkEY}d;Bnu~?Uny? zd-wCG2ioBGLNmP%@8wsa(FBj~7iogWx%>{6sl(A~cwDFGzvA~Dw`^<~zCiFgWm{7&b8UyU(EBclibpkA4_#{sA3dzB0sR2#&}V1{X&S;~G6 zwMB=Dyd%Bhq3-$)>cFdv&Tmx~E=fvxY%T1nf?CyaLwP&&QODZqjrwq-wZ4-U`i^S8 zMF+f58g5i}XDJwuRL6&^vr&UsmzR_-RD%vJ;M7rWRM>a^AzLL1@I&40p&t9)OtkKx zX1{~N`AGM-sP^xqe%|Qdr{-`>1vi@Z_wKIMU%vc=R$cI-ZQLj$x3(pS)#-Ou)-Sr^ zqmCs%h(m4s@n2*7voM;@tCod(6NmzFTa&u#6z{n6L9FjQTXDIija^l*t15F*u^uUZ z*S!kNRtWHfesz{cb^|av^;Oe-SoTQSI)lmH0->7u4x1T7TKQ#X1hZ>Hhph=riDTXJ z9dy`-I{wiqMI(QxHXjm|v^O7BUX#58LnEJbm_%O{?wO!U?xiZ>b_AVve_-iFxqPez zULRzqoOt5V=~6s!Mx`f?D<4;$E`@f8hMHv!qtc}(Vy^z#*b$(vVd*7ltjbKAg{7mM z6ZjWN7u@n^We&V2&6$4MssBfmPoT<&!t#yG|4=miH)KEm&0FCRxZ~v!nO6@C|MBvj z!}tt`7fYwP_w(Rp8-}GB4a@NIZUc4|j)Q*_2d5H_Gq#K|r_Twp%O&pv$Tktcr%yKZNb9P z9G+>Os4;$5LAKLNAU#vQ0{EHqIH+YDUO$>SNbk$0l91^~d$%G$%-@mNWFr6>hUTlV z=qs%v;4mJONQM~!Xu@UBEja@YkM1TM!-wOZ(DSks$_ht-)Q3f7y;xV%M{(*rfc|m> zPt=%vjcm|%@BUxEodff;8VU*aWKO+(P1qW|wrNwCsC7bz4`Yr!`4O5DSpNHGI~v1w zw67KAXe10Oprl?0q+q)7>gIkN*1##e4xDeFMl=uquz8>MX5RfMh6g!Ou6T?hpBZ%L zJxshnu}N`_;&qC*DQ;AJSn)~4A1VG)v0d?xiV1WI<|F^$fb!!CxL?%)$R8)Z042rSMIs1{b;UxsaWF` zt;QbKi1+lsyW0CAUIKdCO`bPn=}%GSj&?u&&TrpOdppKrFKo9G>s%_Pd zJT&2r?GKK4W4k}0ZX4~l2fVS}8&QjMtbZAQ)iyVC)wWxFZyU=}a`s*AC3SbT`xR@p zrPFKMnLf)OAirFc-)Xx#O54fY^3x!5^-pV&M^Jrrdr#}aG+7^}yJ$P)wW!peruR%y7z?!Aa?u*+KmCvL$kALHUL78h(W$ZH* zNI%oIvnX>z`-2m3Enq#!4*{818SYeI~7;KB5$1k8sUa*xnakUZDBustMIP&wDwbH3;O2?asTwuKYxPq zY(oF!Ix(!j>YD_<^#1$TJ~lpd`yt$N;PyP+(B`;j&))eA&T$Rox^6xsHIp$z6;$-YMzhV|yPEpVF&eL3n zbX)~Dw4*kVVI6ga92zPINymYXPx9U548VsT-c>Lm!@pd|_NT$c<&MpU;UA21W%xgd zCovfQegH81eGtPi{3LcPhW`r?mEk{xr(H1oVXR;!ant5q3`Y)er}FzIQ>LE=OY*?nSZ4v^_=1FgZ#pjuFEzA4n0yKbn<~82%@C z-6DoR%`_v1e+2#Zg5jqI8ZTn_2l0A$!SGLKei6g}MaGL5{u#_YV)!>wiW&ZES&8l$ z{^ODU$BW^A7X=rFpWL&>@c)6L;qZgh8@8B8ZD-3E-(*Ur;lvC-fB1MY!~Y25#ti?@ z*i7$uh50H`o6k6f|$K z%>SB?ACg<(IwW^iL0yxRJNsBRI;Qf_f+SjPN{Ju*}J= z49^Qi>saoYB(1&TLHVCSD!FGd2d}uu@%bk<_w2vIv)`S_a4PCpk$X=5`>1!Wz8;1_;z{9)guwjcAM?%T4Wy>OA#IV>SJ?!Gheb})2 z=zPe~_n6{fmf4*A$FHx&(oYS@Ft`TSvMRz06PZ26^*8y5InGCIyMEH8K58(A># zCj2MqAF!h7{%9EY05+e?XCGWnt=)ez_q3f7NlBwqS{F-r&LI}@Uh^GACI@M$E2<%v)T=*v( zG&b6(S5rmEfn$?1w6$!rj#IQX8eWcm%AGkeGbZEryK_vYdDeHwmQ5Hxx-9eV_=X8% z$4o5CjDNT+vn6rd*zse=Ot@nF>={R9CWJoaM@*>6VQ-W0C*p6^W_-bIv&bkt@b2&h&rijGH|cGglhMm-#y{Z}j`z z>Hp4YoIMLpW&ZzMK6Cu3$a8E4+v?5qCJdf<1oDFQC(iUgnMgAbXT1#JtQTlqJ8h0L z=X`LD<1?RFkQqB=%;>Qfj9db_X~oDTM_WgPJx#=M))73O6(kIfcPzr%b7@K-8^ara zdO^wbg324+>%43I&j#yq*5+Q5cXeV-AKpLGvd)3{(EE4XWD$vRJ#L69UX4qkyx6&u z64>kJMGKpj3!R97OKP})5N3DNaLkt?{Y#NYJuJt!bWh{%ogsHHeVMYgUwX%BI@uF+ zknPeH%}`%=Nh@Z3#M5=+!hL{=JJ2q%5*aisS=ng|_Ut)4n417qa+dmwXvRhNQ+c%I zvi6MU^oC^(d(t2%Hz=#gbAXDe-8Idh9=Zju0EPW$E%JXP?XyifGtq!VuEuLj#?$ zp`kvUqlI(q$HV>2VO8UbC7AZL7ZZ!ZK5gzj zsdd&wqmO=taJ;__&t&@DxLOP($MsS(O$(!P|D5sT${kA$edAKTd@P676}7x6k%MH| zP`hGbmC-)0nSL1BKX*R|$$t6aeJ6%g8(y%HsU8vPn&A+W;{t}`=yH_G=KZ)49y02L zJB>+iB+l*hDvKSN^TG5O*Q5u|MdF+EHo>C=ZpN7O$N{n(#xNXzA{b*%Plkgzr{4+q zWZ>w*7~?k$4tDvfQNHJpCd-Fe^HKTE!MTZpQ|DaFl{gxM1!IgK=QP>rm6nF}VtRCE zJ=jjQu#7VSmgeSxq0Yll3d7NV1*}P%es3Y1J$J{n%SG_B^UWY%aQLiNhq; zc9Gu08ZN^?F!v!`4L(hcVH&hgDjN61!5!PrFhkGmVoW`UVO7YQl@BQ2-tD%sj zFKm9C_U`r%ux=d$%#NSmNplYS2>W?xa=_W!#+C5=u^Sp!2IuUjp~;cw3){)a=8V{T zeWjStWEtuwwT(k5Qbac)H>RicN}Z6t7d{yMgI#ROB<8 z@{@`(mk;tURc=@OqoT~^gFk-_GM>!k0}53Il;8cpR`tJJ@p{GE6gMh9r1-Srj}0~;(LnuxUS6iV8!DUPf?UV_3*D#Sr&~!zDMP+DgL|SONwtOCebe>eZ`{`rz@VN zSgp83@iIl3%ZL1)RQXqmZ!7+nVuJkw;SN%ig`$wRs{9MZw-o=Tn2X0eW}IZ&&=9;-iYsE554umg2jL33OnVqe!t#@pwg<%Lo5+Rc=(gM)7ls zcPf5e@kPZq6yH|-Z^gbC05adfilvH^6i-&1t5~nNOz|qk&nw=e_#MTk6n~<)Q!&6x zh542%o~U@4;yT54#kUpzTd@Mq0>(dG@dCwW#pQ}uDy~=jrsCs@&k)g9f1voXy8lY? zDEw|?K1+#srQV?OZA3hu?o#;yBA#1MsQeNU&$XQ@|34!7+&e0Ba6mbSi1ZIoc{%S( zlm||ZhnF75Qj~GIcB}UdU#fVEL{k#G-5>g>$&)6e#dJR>aXJS$c;uH0lww4IU6BrO z3v&nHII>`1zK;N&i%BokDD4l2Hs>-_BZ$?$<%AlabGZW#!{l|E)^aVITliGa3YR5GjIqt;Na%vaSZ03R>Nb+GUN=$xwjGr zhSfyS2@&9;bv$Y?X6RJI?o-Wfix(}c!v{`ykyNuAuIHW&TJ*y-Uhoe#Y-x0T8CUXZ zqMJ*9XJ6;|B@J5wTM9b{mSY+ALH2^FuobY=VR_v-uBn8b13M3v98|kXTx&rHV1Yd?0nd2*m~F|*k)Lc9r#>X1$!CnYS=Zf>tL^kT@QOR z>;~A|VcTHuh204I0PH5%2Voz9-3+@0_G#GXU|)dU3i}G|tFW)bZi9Uj_AS`AVRym4 z1N$!Qd$730Fi!~ehAo0k!;+ZDz)ps(fSmzb2|EwA8ny|x6?PTuYS?wK>tQ#*w!v=xMPV7J1)3cC&VE!bVK@4`Ah@`o*i{i%UVYk7)1-lFOU05eT{;-9x951I~OJOsx zlVK}hXTVm%a(rA3+XUMRy9#zS>^j)>up408U^l{Uf_(&b3+!{STVY>?-3I#>>@L`M zVI7P)60n7^MX+huQrHaaWY`MW8L*YG^I)rCn_ydESHZ4^T?e}!b^~l1>_*s4u#doQ zfqf2kE9|SV+hE^<-39wD?1Bo|6JV#oo(MY~_9WPoVNZdb0edR!OxRhlvtcpMaRCP9 z3(kP8ggp}$AEXP;hMfaD7ZyLT7Mu$^5B5CR^I`Fkx?nym2J#Cohpj?=(FDU`N5B@t zmcWjLJq-46*izV0ut&g-h8+Vt7Iqvg|KVj|`7+{vt%N}{uyp&LV(JzMT%YQ0V-$C^qRNq1MdW&E)tB#a zR9~KbsJ?^hJE*=q2T^^|P>SlyGm+mh3t&-wry8~fwidPywjQWWG{wY1$znXrLdoZy$tr#uxNf~0U98)YBHOU z$Yvz6DT!=OBAb-RW+k#|iELgXo0!OECbBw2R*A@J5m_}Nt4CxNfm1YpY~mWqn*S8d ze~RWmMf0Dc`A^aOr)d6DH2*1@{}j!CisnB>^Pi&mPtp8qH2)gSzee+~(fn&P{~FD| zM)R-H{A)D-8qL2(^RLnTYc&5F&A&$ThmL=#Z>{EEtNGVz{#~=`PXXxwVHpe z=3lG%*J}Q?nt!e4U#t1oYW}sFf1T!Er}@`u{&kvvo#tPs`PXUwb((*j=3l4z*J=KB zntz?&H%4R25mOo}p3qo(N5XP!UKS0_dp}u0$lAT;^A17HRAs;cIJH#< zIK9~)de1Cg=o{}8y;N8{Fmqk8c`7bT-*S?DC4p=qvqA=p<#7h*jx=S z9n6YLKbqmNJohQUbQO;8=>%+d;rQ-0Y2#GVtK^9q>|^1T}8j3W;g z;C)E%4xlmgqg}6P+^6>o$4f>w`F;@&xVEjv{W63bVg#TG$8wlHK<^C*cRx?mm~ceX z#`Kzsh=p)uGxmpNJqlr2S3AAg2rvR}#+dYsY|wV^o~yD;!u;$UDYG=O>AY?~FZRTl zFFhc3;=2646r)11hs36P9lZr{V4V4AAv9}fkmdo%Z!jgMkD^kJ|8amyv7gxH96{L) zJ@jpK4d{0Z+dKN#g(fMYJ3NOfvJX=}Rk2c$V_E(iyIAElL|#Ocha5qmm{3eA@_N&M zkYbVI5XH1&iDIeZ7{!cYx#DESsfrbf(-mhZ&Q`2coTE5TalT@;V!dLMVzXka;&Mek zD_Eb)6jv**QCz2Zz2bVsn-w=G-mciDc(39{#Rn8ODL$z9h~j3&Es9SoKBxGC;#S31 z6kk<*U2&V@n~HBKzOA@R@g2o?72i|j4KCMTF`<}LEL0q%Sfn^aF|AmlSgJTiF{3CN z2hr}6Ri3I?p*US}hT?3+N<~r9iTLwWp08M~$O)RPN0VZ+VyohE#Z`)zDXvyrqqt7- zdd2mMH!E&Xyj`(P@m|G^iVrAmQhZSH5yj1lTNIyGd`|HN#jT33D88!ry5cs)Hx=Jf zd|Pps;ya4(D!!-4$EaL?#e|}8P-xFWl?N#nDGpIgE0!phDvnXiD3&WuR-CF>p(xxI z@|&UZY{g2&Ig0ZX=POn#)+;tCHY>I&E>~Qoc$wmA#WjlS6t7oYuXwZK2F2SI+Z69r z+^G0~;wHrh6(3RDthhz-X~pLhUr^kt_=@7IimxkfQ+!kLEycGLcPYN3_^#r6ihLKz z^;b+NCKU@62PqaQ4pB@imME4gj#11gmMczHoT^x%I9+juqHy2n50xr+;J)E5+&8dV z{p%H*6tlVSp`IPE(*vmRwQdnZ2d`)vOS%7YUk$6 z)7yC-I0JXGei_{-_x;M89S1=U(l@u4!woZqwvVN%Ey(VcX*--PYf!#v?c7%vIy-g* znKkV>sJGeUwh-wZhWLpP4-PU{NnUpPzlFYqLe0fqS!rrWj`AdHW`5Nx~SO_b&L>}Z|&RI$!e#F_D7@(LT zadiGMpr$=s;A~9{%2mgkEPNl&X!JFh03%L3w?o1C640fOQNb_}FCy`=zzL{ec416zJDi{(J!%Y_WhKN)! z4CP}p++^W-DT}CJ_zX^|f z)reFu+{CLGsbG-NWTb-OQRcT7Dj1$(evt}>&$66ds9=y$=kvoEzIb-1!y;0_AYZ1D z3I>@27pq`6o|Wic1;eiq?Bk_^;Y%oys9=zRc)ZC%16#=`SqM_++F~Mg39p3lO{S;} z#@b}z2)19Wf?)#V#wr+AGjNwG7}nD_R>81<-62-NFp~MkDi}y6TAM6z$+)G0;Ryzg zH(8*@RZ9g!GXr<6f`O!mt%6}CD`BZ%c!&O$3Wi@$?yQ30d{)O&!N3*cvdMxx0Advk zzhU8G6%11tttToN{>p%{3WkeWsaOR=5evNs6%2o5u9gah=?ovMVBp*|O9jK3OgvV> z@Fn`jDi}V?wBk(`zDwU&1;cCfja4wnRvKNZVE7ldWvqgsnMqa0n=G8fa#$)DM$+F> z!BEM(EENn_F*!>GgKQdPsbJu5e_I8^X_WV*f?)vy^hO1P{OgESFl=D?_GXg>-m69h z!{=D(4@(8Z!>AxsFswyG=S~=huc<>$z_7>tG-P*_I~=|=55hu+!S!@KOmrAj4sC43 zxdc9k>IIHqSohpva2?GJaNN1j9zgvIhpc6S3(ykoY)O!bl{17UI75{pV7}Z8No201 zJwwu-;~erR<5RbV`&Eh0xEzbQ7Ys#|`y|TQ66HRLa+Y&w4swe(h>$xj+?sdaY!E^9 z|f96N5|k@qzDhc^0`%seu)_9~o?^B=on<{5teOux@ey7~jh z`TY`8%a0geHhMIc@n^ z`~90=m_@|hY<>}4mXGTy`MtY|w@Jr%pS5h~tNLWX$R5aIw>?Oo#PidaR#GuNLPf4o;n| zm`hO46EzG+!?C}e-n?`;--YSn|6!EgGMwA#m1C{~{-a06__e^rPH$a0E+6MenDVW~ zxru{QXED-C^F)pD20HJEc9!VNP5kZI9?Gl=>W<@*utQJ$zV;fQwEd}T2D790^==IF4fD6-Vm z^ihaTJ^DSbr71Y{tk8 z&$(;HNLiw>2^Lz*bRjcZ#ztl}%~0)wq4MH}#a!7OFOH5!(HLDXTC!-l)7aR&Vp&r( zl<2V6M+q|IULVtmn8Q_19Ef!ZsKXF+q*sY34Cj;#0jDxb_i6P6J9-yoz2DYoetS5#nb8vvN zo){?WiGi}77%1zBfwG<$SjTn-%D+_LJu1t3V#uqXSx*dE<`@9~OWhMen2)R{hP$jM1`3A_l=Z|w;i!SK zo){?WiGi}77%1zBfwG<$I1R)l%P;GRfz>L@dSb}3o){?WiGi}77%1zBfwG<$DC>!V zTwlZVxW0zS^)*CUPYjgx#6Ve343zc6Kv_==l=Z|wSx*d<^~69~PYjgx#6Ve343zc6 zKv_==l=Z|wSx*d<^~69~PYjgx#6Ve343zc6Kv_==l=Z|wSx*eS3iAS4epycpyhmkO zPYn4fm1R9K!V?_nUo{POVPFHmo{&Q58=MN4B z`P^hUsDD_rj`1F8#kzl@n z3!d5mmrcIhby{V7~te|Ww&oakRVUkZBpZ8x8=35dv<#Zf*1xnk$*nl0};DD)$;2(yG9Bq0kb2y0-x3bQEg`P3H{aL2k4ZBTM$+E$Kv~0v~AH{?tc3TD?5xdQ@^p4%0 zi)%`D+hKtscKgfBW8rY=9sxLpYuQ2GU=?C^o2t5d!ESG0k`cSjF=JQk_L0o?>tW_a z`7$FlVz)(ye8g^VV91Ew&SMS{yPeAl?FGBdov>qe`}fSD3wFDdA)gP+IXiy^Lq_cO zORQhSZhw(d%x=$T(C*poeyG96i`~8wH5PXJ7WT84-M)}5VfgDH#d$Z8m`IJ|l`y`^ z)OZGtHyHRi8zW}7FJ|DF-CoMTF}sbhxW&A%%_pbsVc?kE?#I9}yL}-8$L#i8`oTy_IK5YTQ&4Ze_ysTZSte$&+vMtOb~{b~ z&g^y{CS$SN_aT_D+wuU2*=>*EVs`sjihE$UIp;?<82BxV9kbhaF<#7Wf1ZWf1G_Ds z3l_UQoTJI z1$u$^5M7H~Q^zoF%x;gQv&C+InzF@iAIfrB?6y$n7Q0IB zYqBSHn_pqQVYi=RhB3Q+BFnQk>^ARJ!*0)FAwMj3dp1hw<{ok^M(6G@Wa>_EhocTO z9PfaECe7iN2?GtygkMKAu|F=J8Z)A$h<`}+i?F9Sej`f1|zMIQO`RT-z8b)W$!mnr2ZK9d6pbvZi^3M4}jP?;dVt9`0OUh5gkHF=^S{#;EtHBcu306;=(tHi_T~Vp zXPlMb9mC6TiIghrKd4wk1A1F{hia)?ppr9smQ>X?Ul`ure zzI0LTiseu*-ea>n9@Tyqm^VZ=k9s>6weY{&AXvQ1BmRuTHp&>Nh`CJB0R3pPdDO1L zWf0cHVLXQWlI37dr)7m3h8G~;e#V$^9KTx5&I7CmD#?2AeKdPI|6EzV6GoH}iIp^xE-8q>}TAlv188tIvK#xKz*Jziovy%&&P z8g9lIzib}$6{~z4Q<(Ct#kq;YG~Vtay{q77m+y6}e0$&}z6uBQsaE6uH-sBz1fU5=hR^gpdZ)l|^@K;Q0MC^{BA~ydy;)SYJL_tv z*NXl!3U0=je2r|-cK;}NRQ}S7jVqk?^vJ<34eP_66q|j)qq5y;`0OCBiEa@YwqtQ5 zh3#sMk(KC-Un}966Ta{fkK{;^nxc`9=Q^ohrM66z4Ln2 zaUC|rj!7)PBWo2H9l<%V`rzDnt*dM{z;?pxT%PnCc+hY#;erIl-&3A(?unc0@|=dD z+zX_^agwv1QX-ch6G4vXGV~;|ee}x)&A({LLhC}vj}r6mWAHd!B>X9o z=ezySgU@?oSoq!#w| zHuR%89G3CB3pWN~OdOnwPSf*vqQ>~~+rymG>lRq{Tw{#i(Qv?{Ei$j;7M=%*|Iab( ziN@^w_Aqg9DteSx@}5 z*~;|jZqnnohbaS2WgZp34~;Q?$74M%=k_J?J=u-#c#(?@aYY{o_1UB2|Qjmd{@-G8IlC1HMcj+9OB2y^H> z_cKqP=W+ga?u>6=G0GjkX#7~qnj}HJYqs&FC1MvJGipr;_HBNX#4mnqYJ+WTIzFm< z_+5dQkbGAF`Fx-bwh2Tu&ryoUs{5IW?BDd~b)ycR?#3thVUGV`W^|J7cHe?Y6DEJ4 zc}&!|F@*Xywm{zob!=cRQxfx-IERUIm#A}t;};a)=}p+q?aE{X7aEduf4*)Pn=^WhBUafl?!Uq3_SZ0n= zFd~n?-GcN9IOctUKEwI{$w^#=!?mz|c!Ebj7%RApStk1OnU)BO^Zx`4qB%;pQ5rD^ zN6^cT|1dHQLJb_8UmVzTl>ATw$IT29&Qapj)FOXW{&=8o&r$Mq9#WA11A3_x&QUT_ zGJlfgCv%kiF(;w!*!3K9W4574iMbG8!im%fTb^%&%rSC_KR$mE%I8NKI6eg*KhnUl zlz!11rCR0_%~3iOk49hUP_MW!pYx>rXpR#9W%|(^CC*3iqd7`%)34(kr5_-hgzKM} zPn{2bhdD}qJVz7x4dfTE zbJ2jDHj%g$hYhgOfQLE1mcFnxoX8<&5Si$pv}&32ByjPH;a@x}T#& zMK~YFIZFPEDK5qGJI+z^%`BuKRYPARCQ_q#C5#x(QS#$CN{6w9<2g!~GH}N^N`A*V zN`5>?=_?Ez&r#y!cR!w^#DC0w$2m%VJV)vC%r~B+G@fESN9hl&LOe(5HTuSLlz3%hZ=Q&E8HsxD$l>UlfNk7j`$)h!%qjV<2 z#dDO_P~5{DrN5z-3j7gn>d$Phc#hI<7%!fqw1b7(!yKhoQ3<1g!*B9ZLzr4TM`c2>N`5>?sUPEZoTKE&8aRH;xE<#x`5osd`PLkz7nrd% zM`;Puw&o}`Gc{|DQWgEJIZE#_e%2f%-lQrD}mkm!d1{U*_sPfBA zzKL-^erbg|I!>ljN&+$v2S+C0Kn*zbNml<*U*=KOO^ixCrzoC5f4UN!Gflg%fTAL(l1wmy&;zrlF)6I70KLV5irV`iq|KT!*#dLVdTlJjY zB!Zy}qy}J^n|s(|a1}je?so#;*kqc6!xT z`4A;aZ!OME9H#L{c&2%x#`s+Y*-ozs>G5AW%eSX_O#MJ0^kg2>7EFklta%blzF);T z`bxwRyo+$V`&ZBTG^S56{(%E}K97m(*Ex>~QKVgV#}RBozpsG5F{XS*HfXzd&k@)q zX8BET2vh6K39zTerYn6ArBM)9`py_jCZ99MlA}|cd>Heb*p@Ub133jWV`S(zJd(ox zG!`<$n(#+v`Nt#ha2k{uJ%)%sTvgTxD0&m?3A}OJi|z)tk%!JZo$`TUn^0y8cdM%b zM2T=3K;C=YFQ#1a7)3sp=uXa&c!6S*;u=MMd()qPd5IepA69%)@kffkRBTuLqhbOb zkMR#wEKw9)4REhjnSGt%L{|gwdX+_21LTb=Kcx7yqUdUXKUeTF{+|?Ot`ua^)c_P- z4Z!2PFx)AMa}`Bb1N=o-15k7|07X{=P;@l_MOOn*7GD9AxK)_1=xPAU$}phlY5{$vgm36imnEr=xP9pt_Gm!Y5P;@l_MOOn*bTt4)R|8OVH2_6d15k7|07X{=P;@l_ zMOOn*bTt4)R|7CegzYH08X$|V2B7F_0E(^#py+A<{-=iL%ZwajpeVW;;6IEqaFn`_ zRV-KcX^JD*{}CUL4B<5ST%pY87CFw0%JMwH!62V`3>QAHqA69~a+XI;Ir1n>w1Vkg z_d*3eeoa5t^BF^zKrV??E^5%&+ zM_CpdbBc6BAy>;zgX}-Aw4bo5U}X&$4*uvw@UzWV#9V6ux*hadM7y z>i1(DDv%oWBJfwY*GL!UaWD&SagOXzzAQ*?ISHrzhNCv;A!ei(K_ilL^lZ-2i#W&A znZZ{Xk$XLyNc>tT6% z@TDS^yeA)$coq4d zB0fb8cV6*{1z5!Cc+Z5XOwWIv`9+-La>o0?a4BbH{$Q5u#ff+gc*S$_AE5N|3*raP zaSS8o448&gT}Bwl+6|;QkRtm144el)M=)}Z|9+_$&@(D4pm<_!9#byL4kR-)1r;D(0s8vd|;lpNntON$`YLo*JGLEN1Y$ zgZ>Az=eA1=75gsX>f8-@9H)GA~ublrQj}kc^sA7;QP;0u@_RFH@{iaaC#oldASo(zL7H z)M}?HSbiepsH}xcwt5^8}H+32P8&rPKO?{gFjVf<;Q`b{o zsPfaUlVBxyGfJ}!c7i()0GuNW5IN^Li6d?KAm^Y9An$_ig4FXYaEnKo&B{ra(2KBG z#8pm$Cwy>8@rLJ(GyNp#HibyY_;Hbi$?XOUHcxbqCKDxeG%@ z2WU9n0QS6pev=rJ#tHbt8RQS~%MOi8GX_Ia(JdeSHlb_mkM5x+ZBBrDP$3^KF4l$k z5htfBrDJ{ay`SD6Q&L%Lo3mGE$2tKzF8q$vEXaClEFiZv6LegCZ5GzYNpV4ky|i9v z-YU^v9xkd5SH3f%EW{5rJ#<(7Bi#4lhu9D^K9mvgS&An*W=%o6==)vHM{e~L+ zBMkM%hl&NbMBl97w-k9qcZS8lcGi0#surR`_D)~CrHJ5TtU4ZRO_sM@Sk+v!4D{`O zrSX&IOh4^ZC)dOO)^W=&S-iZa7KkPiY%*s{vApM>e}&NQI70he7355577G2xDb zgXQeJje7J%*THM}%QL|wf;7x2|NYzPy@#)8(=J4|AF&qJ#KEakh!=V(Pt=%pUI5un zFF?cde!a)eDiM;x5H^6$hfZna*%U**y?)gUX8)Kc)Ct#V;s+S@AoHPbmIK@eRdYihozkLkDKQ z2P%$KJXP^5#cD;-CyVf0io$r`P<%-7X+`R3d`$5r#a}7@NwGg(B+UPK#nTnn zDt=yZm*TsMhL20&#Y9e_KN0B+RV-%zhCAPRJzE>NY*8y#&o6Hb*9Uf|yI|6!@(f5e znC^MD+gS*_Ci$%48=E@LE1~0jzV5|c53cP?KWf|QRJ85%urIe$9q!G&dCQBkKew}G z+s=MiKj>84&|V0efE@(;82q`G(8=7S0UYG`^uW7Hx+QLes7aciLpE}cLKFovr zuQP2)6KVG>-K!;Cqi=n;JRr1=ABUGn$2Ex5v4HzCUZR+*OBBI-fr5(YGXzQ~eTL1k zG2(DRtnsApaOc#4W$@0AcsB@8eB>8|8);9*pr#-F{7~&Vd<)>K+REwM-za)0)L=P` ziMu}K9F9&iAce@J=J5Q;L1ySShY6%&K99*9Xk~)?b}x89cc9UWzB?vRHwC9|bxYNH zwr=%KD-?II&r9D5(CZy%i<~>o`EYd4A4K>EdFsQ=z9cR@XM3bVK6#OKS);5OQl5eBF0rIndIL?BMEgFj5$lvAa%%@ zGJQw^yoM905qcMQuDuJkBp9Fk21_=<)Z^RCXQIjV4f>UbQX=^S)Fe1El#)qu0l`tB zRG6%!G%1t@C9h*DlS8Q}=~J2#N<)&QU4o-SDeV*n}i#jm`pRj>2yK^peI8* zi4v*9?)M4`1C}8lAY&Twbqq|=1il#OgBYWKKn`yJTfqG#@(9zFGfnqO z`m;^qd=ZP4?87=n&kgj*S8W;JQmo5Dln4)k7gKkz>XW>%woZyA45oVPq`EX2i|-~{!}q)wtZ&0}>*9g-^!rmOGd)Liir`c)d<0dbCNkfd8hE;pJi#pWosoKqfoFS9N!;0~#~Jtx4O}T?Ur_1s znvgo2$iPdzD_frHH>X7tFaJhF1+Q%n# z_*;g*!rLzXLLIWTa|f|HzRO4Tfmb4I2^^CFmMhAmHQ~|?cW3HNCf7$UA)ePMF6a&V zx?$^+I%GcmgJW$o;2c$N@1&k(^mDbvf>d87J`WT$^Gc++C3tWi7T!}V5ryl) z`P$kuQm@eW6JDVNu1x)c^_%bAC?%OED*1y8yr)nNuQ;R*g9R$KrrOw+RVuC$h5bRb zcMy`|b2s%rOsmE_Tx3v(zh^OPC1=05%}p(!f1N}Z>hLBeSFbXt!_(;Bpfae#+4OHz z8Pwq?C@)kQ)L|c1f;Xcy+hE6QMu0?K9~K~T&U5-qvSm<*RgiZDpbp#Ea4jBXUZh)@ zf2-Fb;wq;PPx#=HP}3zds_AMHX{9n5W)j{w*(TzK;d^ze^?i?^s0 z$3qrjIOJ06y;~~%6>bh&1e~Jm9bO^<&KcUc7`oL<CzRUg9&RDqSDNQn;1F z8Xflt`RKq^>L2jtJmlvLHWCfu9w!+v@hy_UP|@T*g%ty5DY;FN+@RK-xlNJW&XU}w z*ts1a=T>2IJCy$ffN1Q5b;aQ$QT6C?2ekYWvz2jG{g#i1MYXu7yY3A;ZWmT>A`;uS%0K{mAZUqvwqlWxYcYiil+Iz|#BpB4J zS1nw!q7EHdJ68`F*I3*C|3#iPB;@}AY-`*pMBRMWZrywL@Rz4DmUx?|7^b>Zg3|As9aHPr@5sG<7(E!8JjE2SFF5+;z3dc`269=cFlks}X!JJOZ3bz_BP`*8l zG2yZ~+|8&*1uDsUVDuK%19Qzf3`mWplVCNY9iHRf@{D7Uw1o)3^@lrFo*p z_+16ruJ0J6R|q$j4^g7@vN_zZ!SmOqk_gB2Xzx}8h`AO&VJ8~_X!89k&e5k@jn|V1 zhpZ!G!hy1l`V>94AzV*5+$1_Pml328hiR~=tQYHQ`Z7+P`RFev^F)ow*T@EK_m6_Z zW!uNbl{z#<4x#>fjx|GLmyY#g&x(zorCHLKD{K3+Wnx3!khbAI;Oq}H%#$21pEJfV z_821oDf5~$>>OCpE!pAa02&y3cgIc`0XHgbg8{I;q%(VH%zJDI);qB+JoJS&m3gms z%NwIng*OI{V0hpt#bXtjKHbkyJV)`9iYpZH6)E91DBh>|Z;DSV{z&n4MgG2F`v0NG zUzC(nxP(OEyMW_WUZ~ij_-V!K6u+o=pW?qMKBf4w;;$9|L-D^A`{23Ce2WwhSDd9d zN3mA1Rq=8~{?1~2{#_$JqxhQQ?-d8*rlbGiiYF?bsmR~_^k1y_DaFq!@;4U!f28;a z#lI`co|y2L{Z4`W|HbgL6yH$%jpBbQLVdS{179RKLUFueh2j~C3ltYCUZ!}R;%$oe zD?Xz5tm4lU+ZF#)F$W!(RrSA=_XFbdIp={<#Op)kjLPLIquCt% z9FVaEkZ=;gb&~SymLrP@8_LKdVd7GGqvTpJ9%S# z0|)TVKZS#r#XD2;pz_Xx7%Inm0=EQsXU_D==AEepKzZlyB5+r{bC{i1jub53`5A~Y z@B9*_5jDL1Kn3~-Pz-tJTk&X*c;|@_ly~NSbytA}y#5>=_0!6&@N+uNX&MZss zc<1>@jlA=A#*BF911V+m&c9*CG4Fga#9r{uqgc?0cV5Wm=z@2?ftg3VbHI2J?>vrW zhttcjm{A#XEB`oy9xf z%D^%2{2m)J=AFMn-w)0niyJ73RASiEzRsad@9pBcV0?_9?sS-dmXA`9;<4}h3==42L&cb>**d*Gc* zkel$%Nfs;Sov&mvG4DK%zI))Ek412scRrutW8S%lV$3_wXW}vM%y}FZ?|c&*B<7u8 zrfp3uy|*Fm4kO)hjRNN z?>q@EHt(Edh)%roRzz_34e$I()U_MlS#tXTyz@|AhlqEUndMG1-1gZnRiI+KQnLF` z$~)^f+&`3d?zFy_-=fCwpIT3O=dlQj4hv)P&S4EB+Bp>Wf_08Qc*!DNiY23FS8lUk z@@^RCj{7a|=bwAF*7qajpFbe~7u`Qqoz@9|03Q3}%UoM)diO1f9o=61AO103F8mLp zvAI=x7I(d$avnz-JKSfmbw%xxn#Ir-ZMoIp*}RZWVGv9{aK_cOEMD9IwFl!)*vlQ# z_nvdU{eKDP{9rFQXZ{2-#+;rG2mFSP%scy-b0&wKM(X^HOlA-xYvGKS0Zkcj>X36D z&l5GqFPn27(nHSqvq&#(5(Ap_uEM!pUvk*|kIwQTN>o06{W1>!!PDFi&(~RaHT0vs zTTz{ztHBFSHiG5H&n}F*TaDNE5UwYj^J@s#6V5q-fm%;E=XvNa(@Y_(@>x!D%DNfz zQE<-vOlE?vT$85A&6OPnIsOcdUAhm4b4Ig^VV>lW$=w>m{xZr4K+2qZ%dm4`4d)yU zNFvHv2f$#Qqp@!`;T(B{b2|3(&6q!e%T1nbKi?d-!+yRwY=`}PGui$GjpNfVvE0oF0g9;t;`&x~;NqfCDf=LxKDZq@b8)kr_B>!=yl ztwtE6S%7dkOc!yg5f<@(k0x_=yKXtLyLHQ0u&nEubL<7nx%PtPym%2ZhDK(=av!r` zxv#xoIX@1|wZzGAUGzWwx@BvjGS@BZl58q8?snbsgPcRbi{g;@0s+Va%qehmMLDak zQGNmrs@_;AvYg;s2nO}WR3!`b#==S5i!{v6s?c)GrOJN#1cb}`7JU3Jc2*5z1yomI zc2EV!c2*@P=Y>0~J}YJEcRb(9d=grqMcQ5W`56y<(!?*TOrnR%%#d+w6p3FEcMGzFy&%sIsPNX?sr!G z9MUIcJQQUZP>+Mlyav9&1I2+9v1IR)I3IK@4&f5#0iR>g7m-I8RL(RgNyq^tkm95e z>lQd9xR{iexzANNV2SAuZ5f{trFe01`h-I*$Lm=2Sj+J+goT#lPaz1`&QeuaB!)|s zBQcpOW87HF@e#}}-dR& zR(+U(XW)2e)kzfNomIIO5~^dDa*kc`ycF+DXgOXDkfqAUGHGjP)iRda+FA8I z`dd4zZl~ONXVnC&W9_U;7FM(z%L5?ZS@lg8F4l7VTb5=IJFAXBZldM*n@m66S(Vco zpyhb8T(#WP%kWqXgQw5s-q8ZsWP-2Gc=bfe}c~O z&Z=u!gILQk`x>+y`_jDOQe|j4{)`lCUTOqWj(1i~vhm`bRd1tc?X1eAs=dBQDq5-x zEyo9ojHSx5-HD~;cpgh)?W}qole2bK<=g>iIerMveleCRpG<#iXVtls_q4NWBLehv zXH{xBCiMy}$7e}fgiDp7<+xTvXgTHyAGUIvLd!9`Bu57-7u^h5v>fwZg_h%=pb-ai z9tP4r0)9^7UL5(fkQi91ya}>9kheD;1@!D6@L6{7bK*W6&Tx52+&ANxcoT>!NkY@@0BI2iJt5T|X?_u698}?FFao ztTf+Ay;r`BMQ!1-V>CF1r?|GbISzDZPB>!1tg?n#v&NRqD)akXnVHeFaI6JhB+x<*!51CrSg#VMS7tZEct3LSR;4an$o6aljCKrHqp_Ta` zzDBsQW$EG?ES862?1q{pE3nXcam}iW>v4fa{Xo3gYhB9U_2^B|2yQ(tS;zdj1Tbj}9L$%f1L-(IbBXiw~rAt95hRb{# zx|$!<&D%g16vq$mP{!&rsx0qHoIed~ z8-}Ifdkuper@L@`ADTEgmGrK+9L(votZ6Yfn=M_6EzG+v*7<{?_0p5s;>Rd znLU9ai8u%m!~g>X2$0O2KtNQqBM*fn=m-JPViU+)L&$|h(TflgP^3nKXltuLe6_T- z_u{Kk#2Vk0VoQCCwoPW3O0C{vwXIbu|KD$)b!JW?D7L-*`rYq;zHjDt*53Q9{aSnN zeI9FV{M`Ji!7qR?^20N@1iuwHck{ak{6YwG4kxbXJKGJe@^PKwlunR`F>7x|?eCO)!8@40IyK|7vcL*TqsrZ%RXW_@|X9Asr@rgMxSZ3I-aNX-* zS_day*4nETt|+)_06L3|ZU)6*Ntw;;XUkhADlm&Qb+rsd>_DYy*DhdOlZ9~END8GWc&{Ve=Ml?hDEsE8y2Ydh6VD`EYnXG zoFS;62_U>wXnH_px>bS?2|g*PAC@3q?+pv2kqp!OpbUru1@+#rp!W%__l5<1Q0Tu1 z>b+qRuJ?uoW_oF=s18y0k_(0XrJ(0XrJpxzr6sP~2i>b+rsyCl8d8y2+Q z8y2Ydh6U=qVSzpfF!R-W!vgi*ut2>xEO3^@UnHpahDEsENDQd=h6U=qVS##YSfJh; z7O3}z1?s(Ffu9H;y*Dgqy*DgS?+pvod&2_t-mpNuH!M)^4GUbsr)WUEH!N_o(0XrJ z&<_f&_l5=ilF)i@SkPXdeZAfr7W8zX_1>_c_1>^Ry*DgS+Y|$}O);<-w?tZZk>wWfOA@Oea6nL@r^WCnsDQ+0^Vtl`=@UGQst>qIB4B_@Jzr3T>?|ZHG zn4#m>#eLSg_@D7U_Rs~nznk~#IZ^I4YZHeD93>eMVE%=RYSp zD5W(XnA952#4ii{eg4+Cx4pf~sK+m`Kc3nIG>mu5z{lV@H#W+c+y9dzM+W;Mmvmyy zlIo3I7tb8E1w6LI%|LTB#cF|h`rMe&be-nwtG_OqIjS{&w_)tbf^TldlP~ikKhui| zwWB?j-9frD#(V-eAB0~f>Kc0Hh4_&+Pt?ieac2yiXvdbH2@9Nju8Z`^sW8U`G&dkW|@vPF@gzl| z`p`3bx-0|VHK6}qwu4~Jza}tw8kU&l^3_hYs*{X3lFr$OP_9y&jRZ4!?J`i z#-PT1?(&5@`A&0hSwAn*&G`+=^k!@H&;^ej9C{?w zb>Yy~DC_6*uZ{Ml?|AV28|pV_HOdINor`g43jfhK&&IbH{y=jqJJ1@>MtK9s z+q>?;IPHZtOhFj)O-24}FAL++3{=OD7H8r*ek)=a3u1pba%7{4`u8#B$KJ)cWv!2z zR&{({ur=zjsyi2!PLE=J_0GL{?OoRnZRz^X@b<3WR!Y0?pCd<74eRx|h5A^i?OQ zXo_V+KafYj=ohQvb>Pw4YW_?=ux@z6w~PI82d<~AF|A>3j(gEIZ07*_BnSOE87{31 zs|>ov?qljIuhkOE2L0BjkcYPY2gVr}$KFSN7W7dcTnkx@VW@2M4qw;V9Js!dX`DDK zJT3askt5$Wtj?G+in~;}{r=2wYy4;6Mc(5@KAs^BKA*=Ufa@MZI@X!jHc8w4$Q5Qv z*!>6#P5PCVIiyVqSS_9d><3);eWpkKXPmlCxA_3H{{xVxW63e|OUkH;)&9Jg2W{!` zukDAv%b1Ba#a70#8__0?yzjW>g?J%k81e=A|5bcY1iIf?(A6u_ekZRtBkND%+=%fI z_h8=gfM&n0GyKtX^y{w>7eKydxS|tZqsHFAQr!=Hf`~TrTAW|D=()!ax#Qf!d92}# zZE=okJqIC;Grs-d8G0V#xQ1-+Kk3sWzvnpTm}l7AXdAy}k5TBP4CQa{hp}@ibVyeg z%0+!*z;o7nXgkBO^;Ib4g}AqA^8q}O&~x+Q>$j!!Ds5L!g4aB>2kMu~`uY8t(Nowq z7$+QqKJfFQ{qmb^eU=HDPuq-{F`(*)^u!Ye|-sV_T2qw(etDuUWCKWO?7icpi$nxW{uE>O|fLj~v24z|gSxd(A8DVDKDFD2d3j5_uXlSLW4(JX+JUm=g=_`U4gtucA-Zgwv;!WS zeXcIsKI!;$*>4Vaulrt56X(uj+no?)qM|mQYZZwyx4Bkfm$CM z^Rd2YYB!Ch9h8$S$mt5`YQ5i1xIJd~!Q5EpOzE4$M^Zh0tPOBI$20frId~Z31GER@ za|Lx1eJ^DS&8`SNTIo)4an-%EwM@!Uq44GSk{&&mu?NauQC_815WuiQ? z|1N=C1Omug`eFgDWm{c=`p>xKpDUAB_2`#M>6b4flffQ!E0?;>125K#bsDAT-?_0{ z{sz5=`ohy;4C}{}xYp|3wBe-J@?Xekh5jkuye)nv(z@F!BK*GM;uqkThASqc^bm0OO60=J*tm-!RGuot^@no&p{o z#HZ{<8&S_6B43o7b)p`>RQNLQiLSg^4|o25ByTAr^#*zMEF;H{GnaVLhFoLq$a*23 zgLa&P{&Ch5e>r?a-ydayAIBcYb7tw5lyCp-h+ZogrPZA#)?~Q;iCd*DSts6SB>1Ba zyl-b6c34k#{q>XXc{9&($TN{1!9@)%OD(H|Y5<6MfBkV9oa+)-?~~S;WJ@d*4Jk-Q~Fg*W(UaJDVq3`0?2kpL75J z@A3Zy`-qK3LuUr{hG#upx_!akVZ2CoAWLz3f(yu}``D9>PacQM4rD@(z?NhD&R@up z$sNd;I4enFf5&7}!x-&Fc4=w65*|FW!6XxpNwZUJGl7(TSPYE?p;?m{Hw5YP`u8(W z0x>?t$it|}NrTA->D+wK%rBURRP<*V>fQu_G>Br-1OYawQVYGE+yp6@CMHUeHIc8) zHBiDy$%=d>eJbDH48-F;tmJWcQ35D_DZMG) z*KrODh3|Qj779LGQLHIAwY!j{ZK1&DfZkErh)MS1kzXa5dSc{Aql{qY@ShzaW={g+eZ=goVOWx z777?xhDur}@ST|Y0to{Efi?&ppq5}y)YZAq=fp-z6@6`3k7Z5=dw_^kL+9)3b(POE(?Vc#=9&O z>PUZ{g~A#nI9>|{1q+1*l&z$N0v_BM>dRUvKvYlRSVcE!3x!8n>HjSYg%PM=$}6}z z#QcV``^J9^v4^q3ZxLelN}=bGl$qMbQay#v@Jyh2gW^XPeX@VVtIo%tuZAWLCtQr@ zw0Q4?1{;+2JW}*Ng&|y`yiIEG0VJXk<%!-Bgr9oCH}O9m>C<~>rhNl!dJka;(_e=w z_Z~VNVMw2rgF^HkW>eg5&_XKj)d=)?z1nA@*PFw#dow>kOcf%`{U($12;*m7I;0Zj z5w9`Yj0|an2Rie$zmg~(if?NT^G&8UBjC-gq)+sg;(ypjSOl7vpycL8Q?nzh0ayf@ zmj?JXin-pTeTHX~HJseno0|Ps1Jh6zP!xQ#csNZo)|;ci(6cFLgx(15>n80q%u7a; zRU76FnzecH2-s}2+ULb1mL|^Uk0=9!8|^C=j?@?4WU`&ii$;D2&1AOei#(gMv!=5W zn{?7q=NsnDIwETdi?dmuk62!Tedz9x3$jLDz*=k(GB9!wOSM(V%#mFrx0$quQJeld zbtJvcn>$QeBK4ZT*}Gt~cQiMAHqHCZF&9k7`g6>TnbQq(%&eK%a?}|4Df7MG9Kj@W zM}~Rv1HyaINPfX(KB!4^{>ZO0r-y86!N^A$_pq*pXVZWYy0;!N*(GYzZK)$_mR(_( zJ5BbE+Vpy#5!?dI{IQAcqe1WJGh*3F!`x*K(&Y9&BWb*5J}%t+BhO_yo-?N)It2xU z@Apki@Kf_F9m2cVY;9b)*ZVYdW%jR_evfwszWmC*lr^gKEW!oZ&$7sSJmoyUlg)UF zH-Mn*x0$}bmrXggx&lQ>_wucB_NmNeftQ|W#@&gcPeXR;;Mb1d_<114e~o|D{9A#) z949jO8c=3&?goI`^t>;3vwebs2A>Bb9wX{k80I4i?=c}{Q$}1#k=dzsAfabdz7gy~ zvVfE9QxB7=O&!SNHdOa%{6--Chf2#<1mlQJ$uXv_n0gyNwq?R9YK-B_cGITeV`I`7 zQKvuA7{GY_7utriKd^QFXm_slOA_yK*yM4{yvHGvrw7G*94~nsE$?x#OwgkG!Eh9N zVw7$AAxGu}-PwO4Tf@itZNy1_3;vEM4BHm8_k;2}95mF!&o0xf&WWf4*SxB(Do|GitWiIl296E7wv3f~aw>RhZYJUWWC)Sy-*QuGxx2RiT^-cZbTYiR$Wte5?9;w1;*1*oieE zbwY5?6D_`TLTc^mO6A|08(P0jrTH3Elc#bVOxaZP8sEYy)wS9eD6(du9_Oq3&8_RP zHD+FDd?-hKu(}|3PLBE&OpJ3f3Vm63U8eFp)u!*ORi4s}c2qm3D5nU`_Q;xos-oPG zXY|SB8p4YHGP6a!ZM>@bpcUh*UsVGOaznEV3v(>>(lrHYqv@N9cKxkU;9I4xUE>Q> zsirl%7~F=2eA{SN>ro`MLfovZt)4j6$_<6o?Z`y^1w1#c*@`q<)n?#Bo>$e6SM5^2 zFq?gsst-)G`hUK9?YIjnRJ*4@ZT(N*RG8>h=XhwM7wB)+S;-1xO1jWpE2`X#!;oa?E(2cpmT(A zGNZ5WMsuAfv3C-~UdHS+qi@!XzAvZF==*x=jpo-q8`bsR4Jp_4y0-UvAGa?_*bPz< zTAEMNZZH?L9z({d(3?k)yxZ1Fk3y{`wpv0``{A#B0mh;n&f_tXSy&_RW2|>kFlJZ_ zY{$Q7G!`S5&i)&Y0ZD2AfSzK3Ey$qjnJNruhgQkdB zfs}C7QBu34(l*wsp9EX3rAuI9b!8b$t-#+{Szc3xz$?odmMvW(VcPy^MMW7jT3uB@ z|M8I_oKV(Plm~2kuH)Upbj9U0^>*fE%a&bUQ(>@F%PUsYz~rlBMHTJG5*c)JO;%aE zm<6j@QBt;YF$zdCv=aLRc-o__Xsm50x!fpU*0`h=CTL|>H0V85#dh)Pisj4T`w?|r zp)0#WYNqus`vHwmTTv&W_C}^9n!&$=RpXU4bkiO$E(lxn?)_q9A&16?-!7Ew8JBE@Hd#BoD#~|JbN=htRjg0Fsh$^?Zl;{9+ZWh#{v&3bFnvTrEva2xcR2>FQ=>BIr>bSkuQE#P zja9YsL^s!Pz@vATHKH-K1|bELQc-wiY~`HP(qExvO_qd;29E~gGS&P`j(m(^7m zm5VR0FzS}+cCeKXO7$O&i>bvZnExk?HJ53X6I`w_2R zi3lTIk&_5WevH3L5TK76kgmZYfK11u5kGhN4utKsAIryerc*xNMLT7{DHf=xvp%u^ zoVaU2y7>(_Cs|)Mx5JO`fShZ{ArJflJdwkR`!;AdzfADM;|Sd!M2FuGaqe7$Q{zJL z<2MJ+;l$}DM+l_c@VFl7`mtl>U^@Pu6_jA(+rgvr)9)v-Qgzb3h;&08!o`CtSeJMh zhmh_!M@h%L9T{NsPJ9^ChJZmh=le(2m-S|wxcSYR+}$?XsV|asJKW*_N;}dpO5s7v zV%WJ3cWGFk)Wkgq>&Q=9hXZ-;(5{P-KFt`1fQAGcoB!*H1~hg2;l%BO9naeh`>^A~ zF4~D9etA3A1S8Ex8TdZIbtrAQl?+g54c=Ed2kYpTS<K!Rg|P#Y<{R8f(jIu2{MZafvl2R_jQ3<%)_1xY4MrtZiJu zWz~wBx*FU^HSpdeVY$1sVkwgG77Vv4xCg_Paznx;co}bCuo^@#5|&lF*R}5ZC8!J| z6N8a*=Yg!P6kH|PB)Cp+gP^v9Mfx_OZxQ4xDDt^OuwC#T!R>~ z!qey_;=?YV(CLDH37-laf^t)jd$R3ynkU6xzj#na>LvCopOXw((79;of>fu)j8+t-8E_Vqw* zUk}vw^+0W34}4eBYx{c8+P)q*5OT`=w0%8rpU~RA9<;Wv2mVFEdqHNHUfb6LvxL_6 z^`NH-t?lbUmkO=z>p^S#dZ4zi2WtCz;149-Ew((79(YLN|0byS>_)gBdWC#W6&x)%N$@PeLct2brGoz{c)j3O!FvVo6MRJQNg`zX zS-}+ODCXafi0}--ObMSNc#Gg&g7*tPCm0ocUl7Y&&DSsZ6~Q@ziv%wdTq*dh;46YK z?A3boZJ|FD`maLshL7b=A)+4XLiZDTkkCVf&Jud8(3a4Xgytu*{=XEAOE?`1@wyWP+o%tbPR|cm zvg`Q)bPn}0Xp0DXk&`O@yO%+*b-*5y?~J0YTzxyV_v{3uWLDEM#~#yk1|JDqs&qE3Sd8D)?XL%gkduz zJVt>>*cfQr%yirue||S*{4FiZlw-8ldA_J+ z|9zAL_Uc+LbC56BzY+ZQLJ>sJ<}62PCj1Y;e#wvVH6Q@LB(8G30oz07 z56bKV7MsRkFR+|8(Xj2I{gY$spNVqrD1EByFR)p{Sd3T0Cd$NW1a@@FTYp{uQ^0G( zncJXC+G8VDUuTyIx`n!i_G8tE)3!@!r*fRr)(&;(pNjOl{F3$?NE=2xZ2#Yxaw_-* zVAs1!_(^*`h&Yy^)+qz@5!(ayrtkQePY<4JCA|k}{(v-n@~-b}vDS5xht)Pc`i|7e zkNCIsc_;K_Yr@}!yFW_NAGF6Z;lp4wY_4c;rFOJ;joC4~c}!b-3+?&SQJ2ZrTo=o3 zZ;hg$@EriT4ZjGV1v8>^a1H&i96%qg!`zaMb%BFC--obUKIgXsar`muv3~W4KicnD z>Ay_)%UDl+QTn6h&9E=J?s(#vKik-7>r`gP!07v!4=BSI^Fd%I+WE&E$G?WG!EcJp zN9pzOBZPFeez?)r4{aCg@tl~OU=NC6no9k0bB}SI-bvpB{j@)h-lf-ePS!GFmw|b2 z=XKB_)E^6mwMJE7ZKto_Ts>wncf|@nmAMc)MC+!{$vaKw{aHTn1v1z$E{efUF_mS= zkTRHU^nWri+JG|n>jy?LZc~)M-Sqm`dW`nXDVcuUX|&Tn%EhqbZH==$XI-vEVwLZS!E2u}ub)6>s8E8G~xBVSh=M=>;)}k4GZGDpQ^VTSB zv{RvDV22MMBG6G7^VDCoC;SC@W1P-L`};(X8UBqaKI#^E~0(oAovb|9`<1ElJW}d2R-QnW4N79Wxgi-m%v|B7sPBZ{akofJr^5* zahL+#VTSh{(0va(Y4Y)+eyHy=u*ZMWu+~4b4sC9tPX-__6Fk2QJ!pn6i7$j6#~P#m zV{F^N!07iRuk;So*ILq<4jXob@;`vGxOK!n=m_=|@h0?>)}^dF{ieV!8+{TtN}r0~ zgXRO^cVA5WYXWtt?}L+EuEBMhfnYD zy3RgrRh=K`G8D&_QT9;A>uGP4T8xVwBI3U&<}* zg0VKm{DQVJZjEO@40-L`hc>(&bM3F+&T4Nx$p~DG@zmb`&f!mDUWD%~$Pd=MsXo~1 z8`d3KwnMBx+KJry@btm`_RwSfV-wz{V4`S{Do^(i9P_;xe}w+5-~Gqwkwuq688 z6UzM2<-E(0bIj#>Eu+^Fs2}UrvtE0SF$N54V@iLQ3_*8j8TudUhQOXU$0hvCLVqvbWt?(^$8@&d#hUC`as-v0kfSLHIqJTy z;ynUoCeCz);6L$%F|rGtNB9zcI`F6fK=)3rRUf9C2>yRXBq zKweiOU#)WkHJyE+cO4zl2RfHJ)9EVaab_<=u%K z97}%eHTVPYP3|@LAgK|>$dYDq3N|f<>@|1~`Z=-JAbQY{y#~+JWzm-W&{le{!S`7o zwQl0V9PCfc06X&!P; zSn|Kg!cTH?or5YS_Zs{)xOq;q;|8R0*R|wcgKsnC>2_RZ+N0#s-I5;<9gkzL!9Kh& zX~};nF8ZRq25Hl&_ZrkKo3P}!SiFnu0(sMZK}HEnewM}ar7Zd1M<(2B@Gu)PVafkf z^66>G{|qxuTJmGX^kw!MTtJoyOa9B)9NjGWlY0$*gGKq_a4jE$(#G?;geAXrf03}{ ze~tZ?u;hP*JQJ4uIgIOO$$xx%4gQ(=CM@}XNS@s+`ICDM>NkErwaZzQrr(q9v8hPf zT;`sz2y-7WcDdks$EwMk3ld(xl{%^3Ela~A|m~YaOA2*!t zy$1FB-=rn~L|&V;CI3Q}CTYq4EHC>aOMVY5`G3#mN?P*&H`ydD`3JJlpJT~? z8Iy}8KQ^(;xRmLWmi&jwJZZ^)JyRzw`JZLtq$NM@OrWPc!`W^lXJz04f#KR~a1O6c zTJr0iEW2CsyY?FVF?l2{`8Sf4%aZ>Z@^V@7XEEMo$^R%z;=Tai1v0Y`G1X;fhGTg zsDNY1&uve8*_Qkd{cq)ti z17BotFR_9TXdC-proLc)=6ci>aeO^w&K*L#-dxTT@rPIb|+t>oQLsG;?i0UuG?dbxPm9F)%NucJJh1`T@+ zEnyC@Q!W^GFR2WP91yscFR(4_6KXHym^WIf1ypLM@v*fOR&#V%B2*qLZZ zbFgV!QSb8%^RUR7ribsXH~ELb@04Kn)>znrtH8;j2@;-JCvCW zw+lXR=n4=c%xqkSy&P*mj5LFq7+zTmD$C3VHN3VQ=cCMXc+S(&=J_}s$`oVFuj=$e zYe|hY`2yaYJM3&0KG$3YDg~!lsq+gR^9An?p5rMN8rpmsbr?~@@EUV3!`HErKlScL z(1`Cde6LPFa@pmExj`L9_l)3&HD;?m&$C6TRn=7zx4$g5NlJuR#-$m}n=)zwb(f^Vg>Zl;0RYZUd?iF!mStGt((4 zN3^_g0Ik%6Xj}zDKc-2%Sv;Ob@gLhGXuKXJAL{|MU_1xV$D>dw^CDxcAMIhLvx`2^ z+{kIW=B63^!DcW{55YgU82mvqm}iW=kPI#(gWqWeyqGsd=B1iJTr)svmcm^`Tr-%j zOAyx#78p4NxHhpTXfAKk`;5`)%-jNWda7RFo2EhEVID(&!}fo5x6otkJNgBC)rr|X z4Qp5E&q=hMyki^>kw_TF<6I2lah5QXha_sdI@rLYiE*0Pb<{D9M{rrgRrM8#6ty)= z>~m-Wu!4Tt=rL7lP+bw^|B#@%IH>v+sAa*B8WWt5PeXTWKdiOyPz}2@rEZ3e`^PH_ ztitgsEm-9F*gU1E9oE*rH7X~ly+&{;tk-{3WsT3Zawn+ttuSeSbwVgtofuU0LG=eC zs2)mrRsHO$trJ2yAvF|P7Qp7a5mxK4Q7{KwRrMuB>I zbwOcKs9l{JR7-*r)Zn1139c&)c1yvSs3|kQK5nn^UN6^EB@cb#I;sS^rRUV&9LPT-e*^ zsJqt`RKsh+I<<98hu_HUv#KR|6>0se3eWbQ?r>_EUi z1H^yh1@Uia{QqVVbFi3GJjXf>L1r1y3iiyh+6e zdFjRp_>AmCsBkR&@pIebXLIDpK`jou`&oUP;Ar9~Huv}jOCL-Z!P$(y17>kA-|IZr zs`cJ=DSYb1gZkS7&gh%8$#<@$YF?z{1|%;Nw;qA#m{BFbVA^iM! zgF6e@4#rR8$4Pq?eupse79n$f08olzI!6c2y?C6*YpU^c4yK=i^iDo*{`?rBCx7R< zsfc5|bDb0ZkN6b$j0b_b#Vc&{WTUpGpP5%>?&Dwamn=b*Z zduqX|yGMa9?@ORWEBq2|bABwp0*O9{FjN{P%j!xhR@T5}!g4wZ5Z3~T(&oUf8t5|D zEUj-`USab;id%ssYumCn=~keouD-FMq;hdhEv`p4-JJ{^r*{EZ{alTUE9+2D+m0I3 z9^H49OslEV<*Qn}bm?LphcnnE+vYz;Sy?4*?Hdr}6cGcYdv~C*tCyf;waZr61*Bz> z?z`^kmgi5wzS!EMK%KTnclj9j``rqBjz0m{m_?zr1^dyR3VeAF0*h;FOSDn8+m%2M z-vPAAudG>VZz~LW`5f;7i>nsHb{r;FY`UJV1nQPt)j&rQpJ)1yJ(J6-6H=0BciT@! zva#Vhp|ZiKgTIgRqk02Ci`k9<9CLpy#xztH4*-qWX1K0O|8dM6k2iqiR?dIla!;?- zLdmt>ac&6B*6!v%;PbbFcDoLszmQ{W16`{X#Nu+=;LR^WQ^5Ma0xQUd3bMy=scl%y zu^@XgC*232u^7t28`>YqaeEZ7-BSGXdM40-PsX|iALgBmAlxkSF6F2`0;b5jIOj^Hf!98I z_JJQ(mAZUf4Lba`;@r6gr^aL8x05GwIC1xZ<~ikoN8=bQJ<)H@!8raN5p=>Uav?Jg z;fm|Pxx$LliQ0m6LmdL>fM{G_+1F96=_bmC_pwyi!NJ32JuS0*ZM z$J4&S9nbeU4o&{N9dT%0e<*PqVPDT%0eAd8E}GZ#-pUo}V40P0U3yjP@c{aBmS|12gSGdc(9GaLd)`kWqgQ4e~451RLs z&cW-KI0TUN(jN3xJ?M2k=o@>`xA&mG3);C4X8j)Q5&tM?=RTP6&-I9Z3AA%B%y`#> zGg+g;zd+Vv@hsq4=VR5k97|ZX9nE?k_ zbVvmM0u9TsMy3k_*PWFPZq&hc3Z1~UG_o&*i$6wmFNyYBfO0sY-F6vI1m1$W(nh-z z$kz)81P=T@Suv)NIuwJl1aHZfX!6w0Vf*S;H5Ns2?MesJkI|SPW?-ATC zc%R@7!G{EQ3hox{5ahhZcGcfH0GotfC%6O1@$SVUfIb<*nCD3A?-!8%HqgBO4#9Ro zeig>>?Sl6S?ht%PaHrsI!4AQv1)mq(E4WYaWx>}4-xBN;JRo>b@LfT!yI8Id1wRrz zEXaj40+39b{|Ab5jdo8T>iw+Y@M*e-aF;C8|L z1a}BNB)C&>w_u0h(}K?n7UPB_Gk`@eaX4%{u^6Qs-X_>7n1kgI)6F6xT`h40)~Q6K zpMl4>^Z{~!2%5_!Vh%COek@G7O~M!Au`PXrOu;gW;Twp&UXbfL&uGXKaSVJZ5VPUi zg2;su7A$z6Z}@?k<4_*p5G<eTx9KZxNvOEdtcOMS$A32vGYL0czhOKBOQt$m9CwQmui_ALU`zD0oAw+K-C76EGC zBEWZrkM=DBTKg6OYTqJ2?OOz>eTx9KZxNvOEdtcOMS$A32(SfXjpf$9MSyn;t$mAt zeoAQVTLg4WXzg1BbUwyDuh+gsfZDeRQ2Q1E#wA|+76Gk&ivYE65uo-h0@S`mfZDeR zFc12Ue9sY_Lxdi?ky0d)-fY1s7)h3%lWUxMA8q2t%Zeb%}-?R@B8Wa+Qx zMAKofWA1>h5O^$ty;iAV+X9zrdpg(-!JeOXd$cv33R|mLi08iPw8O*<8>9W0h3mLy zEYxXgX=F=$inigJ7kd-*?#P?41luP%l?TncQ1$+sBw zAiwn9c$gJ#k1Y&?j&#PDPXy;v@GFY6#$P-4h4_&+GitVPj0WIaU?J*4KLNGyfzk;< zp%00K9|<{k!W`s!nQKJS)ZIC9J^p#Ho!$pn9{T^Z)&PqPar{b9tV z8IEmU1ajEfAJ54KQ`Q{0s(~zgYP|^=nHT#>`|BM4pLdqVKM~sWZ0pYqT$( z74=2dM|Zcc@5+Q-VrJ>>@vQpW<2yTBqlH^xPv3<2&hw+*KzrBDj`dw;A!HshS_+%J zMUa_3uw#3AMr(X0etXMX<1aTrW)HN+pMM%Qpg`Mp5akPFd>Y2CKJGGyJNqw%O}@`) zite=9y298m8vE_`!~VbhvHvgj(`xT}1U@ayH({f9fjyQl$2eiVIYvJH)ZW{-7i@^D zR@?hy7hzndNFD*4FT@!3``5-6`di}*VV4+yFCcu;pSmavTW)I|Z2zA^T{a)|VP9yD zeKTN>_17l95HCU+cwvey?AUTIWxlCn@7)VKTJN0#p1rW&HFN^}7|>4v{G6c7RX1Y% zqOHB~^D_<)hE3Ra<5_F!Rm#{yTW>7yQ{TECepH+`C~fb;*n5Y28?(&RF{#L(ZD!HF zGf*8rTAYdNj0ng6g4iF99NB21-}@NzW3XA@Xj$uF7g`-(igscf;k>iFbb1u}=AC=< z+PkhD+S2u%;k12BiF&|?wvra=-YbCpyKx=t4hJtpznbU^+AkXx{EeVa++Q4fnGeSA zsBQWu)c06z%5vh9(OqoY@9}!bj^AjCEi}+4Xv+ZnRP5*V=%3#3iQx1J^{&$|-@((DKtiuk2uL#In zYkWJxUYqTjKeC z)HS@glVhtt{8;#bpTLHmJ}|r%<|fQnoVWbgGo16*j>+5N9QVEHF9Gq+`1glrMD_fL zIgE1|$&7k#h8(FXJ_ z;^m&>7NM)I+vtb90G1Aoe4rIFs{bm{wgh8JnKiIWn1-c-%5@jME(|@h@ zx4`^zjMlit`bqn+T;y3DXpMTYwqg1l@T2|n$_UDWIx@Z&_+uaA!8uJRH{_w0_59oL zU-S;g>gSD9u1kIe8*=s!$4FCTeQY6kzRED<#jvpGoL?f0x?R^T(yp+UNr4_nJrC_# zWnA1J^VHy?4z}B(nAw4N^cD3K{pmnQWni48Ku?{7vF4$!QfRk9m_x#-#~@?TRx811Z(_Jhz((R9Q+ak7NaGyD;Wy zYG13G+BYeTzoBdg)T0*R9KU|dIUKtf-ziv|r33=a3i>YPTc{u9Nb4EMVQv3y@Egz) z^*{#DZ!r(_jt4TbvJHMj{Lr5k>WB6V%NU4E7rlb8-@zY&o%JY4m%6+dx6Fri{|$JeZZRX$h_K&AIR{YQ#~|DrPL6e4)A{@~_}IdD zHxSOT&av&RQK5TY!7^%pxB36D%>R%>j(O@Qrl(%I7yXI3S?iPCC&Lefe{CoA%F-a$ zJT1}Q$fq~j(FnNK!dP#AAF|zqx%2^CuXSCxQtPP@^%L~eSs zddt1mW;v<9w9djh{8%~*bL{@TsE1o;y@)m^Z_KrO*hX%?Y$u1OyM3@s?AA7qNZULP zUe4M`&+F;^WA{V8y`|mx_5xqt7dY+C`vSHl7BPD`ro7O3XoJ)U#zj8HQ+l{mx63$b z7re_)v_nt*eaJd~{e69pwzx^!0($(YZE;I@n=UhD#+eH+mbbt5noQInA{rw31q$j{a+6qs*Jf^9^AKZ4B*R(*qP`vx89;Iq0XZ0%|gHGvDv|~Wy=fR zckUq{Gdw-Y_0D){cNop@-|$b?BfCE7u17llc6@pSz7(KA zHNR@waMB9QOYcC&U%zl$d_VNITjsVPPtNxf&@S{1=8pS@#BD`693Av~T(8>$?e0Mv znAYnDf|wIH20R!G{YHJcG0+8l?aZks1-s9w>oAV%ImWGXqIzA4zTXWQK!0`Oehu@$ zG>j|sKlENgm!kjg0?(;o$O&``(^FEwuKsk9I`Bk(@J^BFW zUC=|jyhe9DZ8-YhU1rGIeJ+{xgv&Z9Yp02B{x9&%jBJZzU59>79>q9ah~FaoO7Wv^ z`e)5)1AEBgkmHoaL_eTTJ?Au!$Wy9Ip13xlU(WgPb#%1({@uVZ@WK5##wq7|JZrP% zPsZaq8ILcbt$Y{$bM@e>A9o**Z$U1OHLn|3BRF%ryA9wMtecL10z5h1ehOaBn3{+2 z`!ve_UC5tve{u!%9@l_+ErvB9*J954@0m}K4sm|m`{3IqJ6)LVgLIhBoOHbZew1k= z_B~I5lUBE7lIJtaAlr~(_t^iYlyjS_obXks*QH)V@-mWn&Byh;Pc`v;=C9E4iN3_x zQy8zD52;&qKSK5&1}`thpnGkEID2g*W5dzGZ(%$q_??_w3;IT)zfqS4w1qRjLf>^J z+8^t|r*UmP*797ddje&hUdWJpex~lg9F2T0OzHw$w?pnb!qBDYhZMAJ3g#UT*3BvR zLsznI4sosQZ;7(~7GWL!5Zk(gZJimrP1?HH4_$zMVVm(ehv9FIulX41pd*gPd%f`1 z>u~Va>u~U%CH)x29JWz-yVvUMYs$!1kj~wYqof~mUHy0(N*U5! zWf+SxxYx!{BR|f=&KTl&DncJP>jEdwrKtPitYPuP`6wUmvF=4#kG4J-lrij>>w~{S zCMYw!&%oMXFy}M}TqkPtxjdeSpo0S@E2aB5cylZV#b+#)n>NOK-u}wG?=NP*lMovrb7 z2KsDSr#R1c z9ob}hJTK~3jeBb^{K8`X8)pVuqLv@)SFA%#|1_?Vy6_A|%R2n=ok89K=m=$%b{f{& zxbqCdXln0j!1FH5`GbMbAN6>SY3xTE#67s@scLI~31eYD%RaNMy?+IK@^UR*NnL4# zA&-$5?%N;4bI!Z*yua(z_gWvo?=GXG^nssFe&J#K?oI8y@d5nq>Vx|xtQB8sK>eTp z#tT?){rqJ<8&iQB@c96qAq9T^^4ZAiBdm9C&3vkJZ>YU%`tT*4o_@`pYyCx?7W%3W zbh`)nnEp~egWhA|Ik0J!Vq6t>;lA_RrnT*YMdKWm5Yj zlxK7+(T_&hmUBM8WFCB`%IAwmz?X7@e%j|LZF$iwUHjw<;6wdHeK;1fhp`pgjkWO> zrp0~>e3y`oXK3)_oT0+&{J5VQyf0v{+k1|O><*^8BLnXxJlTHuhz_r#jHd*V$350f zc-FT0U@qn@6&cm(Egjuy1fGcZMI6h7_b&U3L@%?<)YE((X5v}ZJt%)sgyVXBr&-z( zZO1k4_NkWk`32G~L%H2;Sr6Ko(=eVA_e+=q)s5r}S@M*wjecF10}JZp(NtAgS;;lF zo;%^YlxWurDc-am@yRjsZI|S+T#7ku`#mEDXP#R?EBt%;IpqY7|WPmK@DVbP=@{MShxl_GWS)toL@(Px=J`ko z2bl+v3=T3=P(8;%=1j119AtV&^nQb6;2fmuJ(s7)aFA(V#}GEnSPuOQe)GuVXWIh zrW$Ya_on?B3?0gs=D`IHm7ca7^>rxiAXDYcM386X0Qd^(L|(r6AhB$<-T&aDSIln) z9du@?ytG#lt|kn}KkHG)B22U?Z(3iZQ~5UKOKU|<)FhiqPrDC|uOL9Y&Yu<_b(&2L zz`y}D#ij?^Zc~|Q=aS3O9Av8LX?R~{s2O8XGL{8|p=Od&Bb&^$HZn~*$h^!1 zS-@JUk?TB$?{uWw3rf2<8fN&LKtGI9wV7(Z-LeS>nXj=x7uf~!ru~tOx;e;HU&=uy z{iyj=MH;_#RS5@~?~>~2Aaf?GkaUpw7_MBTzSFm)-ca9t&aQP|nC7VMQntP&R~qVj zHsMPvBg=$?%s-GgFI*J!a=%^)11sr|KtmnWun4p&z&No2uQas)U2gDeUiV4l>oVHkTP`70mCa z_H{*R_p+P`2bp+fWvGOM%-@qrI>=myN*srS%u?|G_i~W=DGF|Q)e9LHv+79)nToCC zxWM#g{E4v+>C5o3_70hz@glphyMs*i70)er0f#Z4!EbR@(n021Oq_I(xrZH|bddQj zW0MXtf60uJ4l~7gG_t~Vknn`%yY@Dhl5P@SqGWgYq2t42iuby!QyXdi*(i7%+RVe z=^*n)=9_eoxtgu;MGi6*9AsX}woN+7{4?1k9c13c*w1m0c`uSX4l)%SWYXiXN;=4F zA(?cL`81g)9b{g}7D+nD9K_h9gUoL-_NyMHWgwDqHDi+wGM{1nx;w~JU-SG3ZSEOa zpYc4~KItGcgRDwDsbEFDn?c(l<#Leu-z=ufLFP;B8kd922g%OmAafVvT@ErU8DFV$ zS0i_u8I7br&q3z3NZ{-3!-i5x7kO}rG|o*AGzQKEeMrGUCS3rlq=U>HmjBB-$b_h# z!m)~OdI{}u%Gqp`e}RL{Hk94xILQ1nDsJ{te?bGLojC_Y%Fl4>_{!{sq#oN>=3!K^ zkL@dSJ{wE>$~=KaGRsj1U++O_X-M4r6oznS9Y<>L0W=uhd7tRL4B@Aqa2NjXLHhLG znQ2?VruPtrFnua&-+SnAgdu&}R1~83Fq`t>YPxvxZbYEZ>wN{By)0#7&D?L2<`KrvymVM4Tn)a) zXfrabv1S>F(@7Ku=p4j+ld0jH@kB2@V4mo`9RI^_?u)QZ#(300dqjge%(-qifRI^ydnHYmO5D;`NFhwDxHb5xt&GP1P$!LQp6GB3$0t2WFV zbXm=dv)~Q1)jluIf-6ZmpPy9*4ma9YEF8rxjm?`(Hm`ZnsIh2WvrS*5Hob0)p3Zt~ z(rL$>Z`sD)Qed^j>4s91K}=n@I4rIO*35;5#~8Jcx!rI63AvkdN9`uF2PCg4 zqxc@*d{D^zQSHq7Ax)YKMm@>Mhjpzzo2)D?L64Xe6SZmQ30XDEt}x7<=3Tm6AEjq$ z|ENDUAJFt|saeZb8s;wZDNVMej^cM#=HtRGXVjA{$a7{4(J5$6G!y;Wnm;uU>JUCy z7$0Xd@AYP*rsGRl>pkAvhT!}=46pR8!v*6fkk1~^4Ltu1i+_r@6g3`y8PoUovi3RE z6(~x&mme9AAJ1GCczOSk+lV{J_mEva_;unJ{1im6&x!c!&%dGg3ptTiHYl^$ng~#v zcJ#5%wokw>??y1{y;#9(-;4DD?Lb5Jqz}Kv=5h7MKKy7M5+Za9y2uG6E zTD=S(1%xV4)0meiA&j5gg4B3n?0d<50cgs|We$TAjRB0;f2S3o+*2qFf3#(G$^a7Y zWuv1{r*?Xe%IS2iecq#HI-PpyJ*uTMv`YA3II=vmf}-*vmEaj#hW|vihL4@I5huD_ z*umjM?_z`u=!R!|TsXv778{I$b_3h2=QAVkJ_Zmcm9kYXV!+J|Bb7sXE0N)Yv{7 zeLfPIXQhyz;aiXYL>G@ShQA8h{1%J9+MY!?gYZ#=d8wW?uC`~5`Sz@FwLNPr(6h$X zde$)Kjs6ez${JlrBYY8BNDWhLUDGrk2WO8FLzw5^#uu&e_qh`j@3Em3SFJb}hnhOL zJ*%w^Evcys1%n|te+^ZYm4y~BUz%IDtga%rV)2ri(8OSVFhAt@qW#CpvUG7-bxmCb z9dn2kly+(Bc&UZ-INog`BMI-eI7f=|ib~rxt$4acXyp}GE{BUn1f#={#P)9sd2{^R zf*~AcR8&+{*4v2^JkigX9uSY-f5SBM&YPH@sinQyu zsWe}M`WqT38}3w5lbi{6hsv#q@UEM073Emhqh+ki$4;yXsS|>8o@nu%6H>psrqU`5 z&4NSSpsFoEwbU6~3ujwZ*A}8a`_G+F^=Q#-GT~(k0m6NJGOEPw&fpUs+ib7TD zku|=ys-oPGXY|SA;8r-*Q)?E!?%VX5uMtjjJ#)<#b;x*Ct$0;6`U3EDYvqP~jj#D` z4Z%yX?>;rKU^Y4ees9(5M#0i5-^HG*`&TKyZ>lFUK>cF1?|JpDHNL6nu|L82?d{4p z72b2vz}w7MRcfWG_XT{Hyb_v?e*XO`aD!{{wkq{L^21ea=&(P-H}RiWA!BR2ikn+& zt0#_yBjOj}rgooE0IuJ=2H&gCR&RI;e48Fci9fo=6E>@!gooy>zM;NT)w9?57OG7u zNKe7hRY86FhgFpt`&Q;oLd)hCO*l7SrTf}cLjhWT8+vQvYel}Z)sI&-s~c2-`VbCr z?;IbxtWf4Xxi_7s_0mWokc@rk)Pnvw%l+zb4DTB_ThRCjiqn@#b zQBzlG%s&5ISDJICO`kFIT&4-)SGTy%IRBjUwSO_Jo4ff09>7F$gn={q`ffDwv^2*R zwjFaBdNvw&`Yz~ez{m1e{-P1150u7zwxi)WdfN*$6M zj_S?Vd8*I&CajL1#xD~q;~e~)BOUWSmk!k^!bCX9S9qQ_yE?H1F*t# z4zBpB@$1M0fBbgfw;R7(5Ox^9TKu>obdG<-J1@L6)vmbOz(TELaa~#UvgHPrB3z4o z#%(XWyTX+dTr}ApZI9PUFYaARc)wE8%~@{`H@z#%Yv?-n$})Ebblkb3q71HLmo>t{ zXo>AgR{L3{r&`;GaKQF#dMr=9J-zd4kDJ;BuDI!C@!)Bjz#qwoz0QPGIum@#R~GMov2Py<%}~BfRv|qwmsX<#gL64XV8We;HT45Ev|m z&?Cq6>B|QH`yKn%H7;FJ0WV;66<0!vmX)EIS5(0n@lv!oUF9~=>1u=R))$giq7~KA z{rfUnd)8eFFS+dTCHCkyzP#gKEhf_6SXY(Q)Rp62*Y?p`QdQSjR+1P&brmZcDvjmz z_N*JcWa%;#AEk!VXc3u`MtDM|gXI~mn^j4%o#s9X*(DhSLR zU{o~I5mC7eIwE)r8|Dg=12Zv$LZK4iT?@s^igYad=k<%y%*=|^N=-}CugI(hhAAsc zOG`7;|MR^2U3;x_V1_f?GQU~B-#O2I_gd>+Yp=cb+H3D;zwfFQ2WQ#NfAQi77iUXT zeXE@7U7Keb7rN2pvMXQ0IH#?iG$cArLu-re9^v(d4u@;Hc_}YX@hTji-mvfBx?NFQ zwZcOZxX(47gj@I)qq4Fqhr;Obw!@<}sdx#lO5;HnC%&qY9k!;iu4)N5o$lTLva_?^ zl{gLT=s2M8%4w;qy8yH3mWvv~E|BdVSXvh~S=fpWf<|A`D{WZaSheH=RMs494LZldrll(~eFO6tT5%!3 z5g!@?=dx)T3KK8mlu#udY-@Ni?m0)n;@`Ty>Vi6NNs}~BINo^}${H{)td0+OC zWmOzT(W37yf5J`lRbGsypIBV#6)%0)xfk~Mr2(GsJUpXI;2S4*qJlY!1kM@wF2aNt zXJYP#$3eXLlKj~8nD$VW@dzoQLO*3tQPOgo^C_m^Svt5j(^_c#hphrIzla^!Ka4a{-O!M4eRO|N<~{NTuwEhQVrH!7~a)LG$i zQ6G=b==+f73FnY#r(--Xg?%QNvN$Q`_9Ts%j&!vq5s>;Azd{ip;cldBH3E?77|1)h z^&5lwu}xV&&ZABJ_|DAK0h?axSiD8$h6WRNIb>JgInMFo4PT`1W^9{0RB<}=rMRKN z#C;C3t8W4H@wBmiL{q zXbg)gBI*LUfyC)Yv#5Ohc6?zk#9c&}Fk5WE0D;}3m6d1n{zcT#z z{Cfaeyan4PF#ZxF04clI6y;B5#eXJCzBx<&e_1lGTN9}70V4n@Kb$4=dNYCX|7!#w zbv{ig%TUlGP zxEaq5u2}bxNLIYBT`Q)b_4SR*TI4xGrtFv@@Zkc> z4%F4*eS-0kY@VAjbJGVoY~hhlo~B!nu%U%7PLRB25e2>_sjO{nszOxsKEiz%h36`~ zbUkfz#YYt%Q+!hKX~pLhH!HrN*si!u@kPa#6kkz% zP4NxIw-n>JCk!n@|0RyXI77r_bSQ49Ll47CFyi6x-AEh>VpeeqCTYWPQLF^1gfEc77CE`A@3b%lv(03CswjZu9#Wo_++@|vF zigzn+RJ@N!T^Q_!rZ6u!AjC498By_pF4K8yv-SAetRXk1ce8okIYZb3i{G8(Lir-XxSdsS!mixS7r{bR#^Ee(LUVO0vi&Pe0 ztdOf!ZdMdutk};_5tvVWu>xiM1Bx$Jpp0w4pJ}>I#lI-ZIE46QEYu^uSRsoqR^X|+ zUwpAbu2EThu|ocm%6BP>FIMaqU#vj!#R?Q(tiXfOzgb`L#R{CJviM?!yg+5~#R^$` zu>$YY{SPaOFIMaqU#!44b-(yxg*+J73ClTNQGBsN7GJDD@x=;!UE|^3N75H6@-rFc z7hkNvsVa*vR>=H#mGMoA;)@mXbt-RAyi4&xMSlOue47>dy$$6z6!Xx5Dbt@HQGBri z#TP4Zy6zWWtdQ$f=65`&ro@u;(Xn|LhlJTQ6yK^y zf1k>aC_by$p?ClWNS2dQEKxjOkxMT!{&R|VDBe#*9lx#eV=6zT@^eJA!!K2CSGiN= zmsI|%%5SLrZvg^2 zL%M&H;`55XQ9OwEF{Gz&ah5k_BT*#mY~x|4=of6>Mn53O6sb_zm z2RqP%urHqa6#Plim&d0%z0_wB|8!@pfbsix_?uR3r448t{%8HvN$@9oS!XEk8gwQYF?_O-_5m}%WI;bZXNvcbb_ za@bAB;p_D$s0R$W!nq#qLo(-k7a-@t*>$1uN%lU1fzxYS>bHE`ONlf$iG?3XW9I5zWsue?@`@e2ju7AtxlZG|p)oD>`Cjuwhpnhe1b~ zH@E}S=7YUWpXz*a1N-<3(wEtf*|($qi!$L8b1VC8)Q>k^_Jfz<(}vfDmwExd-F_y0 z|F5iv_RH}ocP{R0^qpId-(wgI=A+$Nm&T#+!3w+KF}TLYr(m=1)NM1a4VGoDkLPfG zq&Lx?d45Me_Vc^fb5TF)83H~1pojU5F8H97xH80h*#CXVZyj<=X93d1R$h^jb_?R3 zem{<#{ylIEk71j>Y@mB!zfGzaV+6;HuOe+6`tAYJujIk~Lw|}=&MJ%>i2HwtTL&HR z3H!$@fd$Yp>Jlwe^}v55{MHTbfWJ$aI}GmlG<_|ElmWLEoMm z7d*1%m#}e1uY%t}*m`4@hucO!9{<0ve-P`RRaf}bk@Sl+J$zjRaU9djswalLFJU`5 z@tXEE>9y_Do!)rT*57M<0pjV86K#z#^Sa3QuX|q3$9cI4<@o7YThAVFW`_qK34?Gx zQ~3Go+P0iMV%F-jzt^^A5RS*+c+G0R;KbFQ_rZ?g7vsF)xQ60a)^^?2wR{oEep&W2 z_;j7N^(5G2(}(rPF#fGX2fZDgkyxwS#{u8MbK%Z@%-7X#yy`OXTvNnscZK;`dAdrM z%l=(0KWb~(^apI?Tf#P$_FXqa+P4V(>?pJ`+ZHDjH~(W z@U@LG=`Xn6`YyP z&`sHAN!i=$LD`tip53t-x>$#AXdV2G^uIlL>-SKHg66?nF@7b2f;K;1@bQ?}wmwl> zfG63uHe5L$>?ml4Z-wTzjw6t^_As{74KS8Cd&?E@8|JZoWzTMt`&0U|&O1-QacsJx z-G>hhxsPFN;r(LK)bdV@ds~mtvG<6dT(@-!=1ToA9@F1z-!$$I7^`{jps$Q(>c$v& zBig?Jej%hy3a(BRaEwJ;^H}_iv$n?Du8y&8=KhE};uXsWJ8kFfUgtTuk7ABD5bt#Q6{H4leND?M+JxgxSGVWGhhl*G z&p}<>artTV6P_z#8e_QJQ+Vv~cY*V=73ZYWTkuHdG~_e)*u!uyVjqIj;Xfvh>$}xY z!iPG_8_IS{&%yDv4LtnFf$axkACG$|&T|Uw;o+PF=|*n|d}1e$>Hnz0bvK z>~X`dCfhHL^D4)lUh^>9T;{{+%YN`C`yAT(V$=b8rLFyyKSG~5wplJV$QP7hp480egR{5rynq9^ll{BzrZw%n-c6hLEnC~3+_y7W<&-Uot;Szp#z{iDj{TKmEiuwjI;(Jp4cQ?JmX( z%$*L7H=oBk;F_dQu_rpy=mT<{U_O#YnfIf9=33`9PM>Ocj!MY&?R~0q)eX3|+n>g{ z{w(H&zxn-fZPy*(r9OzUr)}V^BOk{6Z4RGlI`DkfZ~ns^2R6t!aG8!%9K-kwC1X|k zigvwD;CBtz|JIXa-q@2dpb2_82EZ~Vf!CG&c+S`!cqlUv2IEV z<9jvg%JU!2QP+gmueZ80U4Xub>zV$e-+-Pt&NchL-!@bHeE*fYah_jUhc+(2+#ctH zbIZVc=J#IP4HyenW8H^>4vq)xN8s?odla4@y6Q)m#z*~VR@9F&p8@zh%RbXDT`S`g zeXy}FvG1@y9meYh_X@lV-|n~$#dT!bXdd*$z4Nx5hc(v80Un3PdxZA=*Ws^?bN2UZ-LvQF z_)4aeb7FHYsMjz1XeY<0pQBEu{u@zm`cE8+b4}l2`J1ZRhitm86JsdObK_PsPGQ}I zer21k+d2qo3Xi{bYk;xG$9)F=R&OXge$5S7JK%;v>FYlAV!?>cL0;$&`7x9;2zuG4 z{DNz@_CwpV+#%56rK;NDJ4)^eUfFu-|8fjGZX6@W6uiPZIEjALFKX|#otvPO&nUyp zaYMh{PcWz8*tpSqgy-pxeVsh?#p)vu9{c1xmuBVh7Od&~652C{G+)f>3-o{W2-a(0 z-{hJNpG7(!V|t{E$Ku}4yfvN2qI~LF3tg{G93j_H;LYE117!F5l5?HL*bX0ieE-9> zgT^5*ec|`RxH|0Zcb+f4_(vTDs6TyBANTNx&ef>TTj}+fhn*?+b*x?V#6ZU5nC2fa zqVrnB{}u7f$NRpIxEStL?_lg<8a}(?*)za%V=x|bBHqUiy!b~0U&lSF2xWxhH?9-h z^NMf|Wb9iv;{eR-F}!2E)Hy?c=r{9*zy3S(Ir+~h>o2&cuR!J_x9)x;H892woKhSsU@cqv07>T&yi1XK7wG|69;GCV?F%h}-bXu4`Sgp-dEpyK-!IhNORKlIClruEm&i0bQmD>~rQtsn0N z=!;wEZ_cFu=bO}hUZk7v2nS>Adj)H7@%|Ra8d_c%#{UYu%Ul2-$Iaq%t>2V1*3Uw} z<+WqRr9LUHKZAM7)7Nc%1#5KST*%nOHMtf*UnF3$?i1GTvJ>Jy8^FD)n4|G9~__=fH!1p`s7d&q2?)9VQ_1uqIS02w` zKkj-w8)hAK#rom%yHfQ;HbcCOjezL5N26CY6~Ugk9?V9$9?V7?-(*GBH`$?~OR~cX z;oOW~(GDkhFvfT#8|&0I>H23oLTd}BW31jdt*wgVZGA#7ViR-+;?=F#XgrLe?;*r7 z?ZdYL`f(n=!SE~bbYt9#T^%4v=vItwl;BqEhe!^$V)r4DaVypjxr|$}U{qgzKoX=* zM7q9%x!HwVu`pMh{xOVOv0!xa+t3>f8;Q&@INo@Kl65N~wdn|#?L&qQ;+=?9q zAr`t7n;>-&w_@$6@Q~Qpq|aE_typYas6U?kG87sqFDVSZ#^oo^L3R@-ZpC7`xD`vF z{G!;zpjw_-8tR_q{Vv~I;JkSVqkw_>w#%GUX@d11>& zZpE;0nHM`dtWZ4p6ct5o#aNeEkKBs=1BK+p>XQFrLq=}JendUlZpHqW1zWdbSm?@| z9}~A?G1skF%ylakyCbYk ziao;f7P%FR^9)CB#fCF3!>w2>!>w2>!>w2>ax3yhrHbSC$Zm~|^w$-LIB*s)A(-HOd)%UQQ#^B8O0imhX#Teo88`w*>Lu}|^1tXnax$LGbY zTd_l#*SZzEig~SDG4Uj4-HP$bj5%(_rct%yR_p;DgyUB1Fluw$iX|y;?^f*JRO`4E zy8&wQV{j`bL#uTwHj{O=ZpD7gY&&x+24nKVf3dl&Td_N+#<~@o$VzwPR%||#_Z|Ey zUJ^eYgIh5=DT`URV&pSp)~(pbnA*A(!;~AtRE!=XW21`-UnE+$VxxKGTDM{+Ft2qh zHk#GWbSoCKZpFUJwzqD@Zeu|UtXr`r>T=wQ9nW~jt=P9%6UVJs8`U{(#o8F}xD}hn zc*m_+E#>aqiY-HeT-}Pr;8u(tL}J#h*y}96r*6e!a4W`Z6{ktuiv5BIy${@q9fG>| zHEzY8!-4yK5?{js7++%irmj!uOYE!I%nN;q9fyQ{#g|xoAriyAR}1vOz1ILhUOeCz zaQ-joxap9)@o|m61c`i1?nWXX!wZ8TpFi}w(CGIOKU=Zurwpry`;|VvWSBSf7Bm1P z8ARs{qdS{EKD`RXt}h)fes1zZe>CR|htH1vM6*A4#2M7s-`|Fk{SOXDrU7B9^M=Fy zRzaBR>=6%B_x>skr(c)@!enzt{12lBN)%i{jd%ga;|~f`o;Bi5N`)FZKDCD8fg%pS z2(=jO(`OkxTR}q6L;SDv*v}hL&vXZcm8uv~%(gh#rx%{!`nJRfPB;Cb{v${WM@A#g zVFiZyk3&RsFkBq<4EOm>6Mbr>MlpVbRChQ_85!36%;7B%M)`kak!KEXfH2yBB^2r# zAr<-mpfq=QV=bgZ{kJI18BXCapI^yBV7kNoJPKzHZ=^KV?=Mj!=orqQ;E#orz$T{i zeYoeT_(pEe#-D$#|0oV+fFJ_X8_;T>7qw1OFw0JAFNB;wV3?uqs)bXDWCSyBxeQLyKss2|g4ju7Nsxsn` z5ruXkev%5_ zqrA~v4g4k~8xE69IF5^_aT5ESw?6*C_3;s3r2gyUteZc_8@&KYH!*3;GI)Edj!BoC z>y2KH{hK2dUxC{eq_6ObZovM0Fn;Cu4PWZ{7l%12yhAxd9Pv3O`(AJ|3#?clKZE|N z2E_U0;ea@M-C^uM)O9#^Jb>*y_83kvWOe}W9_$&E$E-mr+-L{H4-_42f&uZPMbq)} z&!ZY32QnajEMoJDaMt+~RR!K4W*jsKJI1cWpU)|b|BhS-ys*a?LOlP7v)QlT+4$dtO z_MaIv!Q;!}#gj^dj~17vpKlwV-Vn4tC{l0(+#J2Rd}eyqIJj3TkG<(1RNe;fL%;Lt zCk0P>#f#w^=6?9Wfd`&RLH^Bfzw@_AB@==H#X)m%@G=|?-Iw@z@Z%LXPbwK-5)4O? zGvUqW{Fx==O5ic)Iru*sS2A%tHt&URnS8i4N(UdB8BCP;AYB~Hn^}H(`7Oa)m(08} zD49teuR!PTFPYgeF}SEcIB;h0p$CIo;g07=iQ?e874^a8iJOCqW|pUqf-9T}r7WfV z^x&3DV(WY#PL4{BDw#1UXeowA9qzyB(%5ajkJ8h0aTBa~@W>-@C=1iG9tzf9Iuq`f z@@EDYKFF3@Q7jRseJA)8y!4q2-O9T_+BJJ{`Nc zJ^3_KL%ZummH&)A9_}vL`#xy?odN8v*c==;DFA9#VUmFap zM|7V&C7nTHXt(+NrI#|7S!sH8XSp$W^$iKm#(}Y}1)x=Th|lpLzJZ1KsfF*qAUe_~6au!54fKF<|Bw;SOnfc`$wE z?2-u+CY2l!96NJ1YWyKYZbElB?dL}YkFT6bZ(QR`rGxy#&uc~LKfZWo(2fLGUo!KS z;QMge^nibJ-NK-${tGyk;NDAaUKrdPs}D9Vhcm9?HEY()m=LtgESa$o*VnR{C3VFM zxht0E`F=mo$DkVEv&95#Y2Yvs7QOvo&zkS;2P@G5Voy_dXk#s}aoo%@{2B3mmkGQH z!Ee#@{!b@%=(7ml=SpC@3T#d5KWKXYA)oNCh+Q6B7GIm-t40Rie$qhKr;*9aJIj2D z8)P$9^dB+<`(zOE4#%6s3f$OWzo=s&3=E5LgMt$z85$w-_G#li-v2ZbYVd(+qzr?e z2J=FaB0r3L3^&*XIFN_%tV__=^9aojrg(7 z&*5hR^=`v&NA=)GS=se4`CdWY7;o~K{k!9_DRL_lI*>_wjd1a|w5pLdrMu#wDbq0% z=8=t11@}XAOQc?jx^T=SE^s1uN$NgG{lmx|v9cNNN?^`f)3l5pNpf}4RH?q1&=JMC zN$7AYD#$g3t)x>OI8JG3sjOLEg(j$l5V|LE+%dTwzF|r8vewG_s)oj8OW{QYu0+MH zQd50BYmkC~Q;MoZ7UQnnwMxh%PdU&p!3hy&K7gtFfwyg7|nwok#`+@JJ zU3a_GrMD$G@oG(F>Sg8Ns6~#ns->p2VR4-Z=GxXKLzOV8aXl^V(8LTy`J0v4OLxO8DCeeIPBag0G;c&qPfdc6D)o zbWuZX>ms<8s&Dm{Nd6u9LqY}R40P!OX&KyGEml&$~rDiFsT+&>% ztg&QLI(_7XGbW!{(z3Ls1jmKx>4b^J6HDQFNJV-!ttpXdZi(tEhGqTYs^xk;(Yx10 z=+Kokn%ApY9V^LcxQOnaD(jn@T9_hduSs%ZaLr(>$#$L8$e{x#fFsaS=RucoH4wW=Cd^|GZ)QRm9YkCOO(%k-wyjT@!Vi&x}8X-QQ}IJn7GURl3vNe#M_ zj63L6H5c%hm&lo2wq#jLo%%p(XsM+yC~+JRU2r_2s@e}PV~lvBf-@^PZdzDr9K!W_ zPik>pELAP7TCxzAX>n~GoKjY1N_2)*T+_68F}r8+iF0NbSGBe-ZKz(x3p>{UQ)$fd z@Df8almgu;jhX1c{7n40Xfp4~4`2&Z449Egex}2W#XD4GJRnMV3G*^Huq=jg_%Z#? z(xs8c8Qs9 z>SO#0MYI2Dq~n_~6PS)+8Gdg42BCgs*vI;D#%bzzHMY$$VbfbQ-t%U2LxYLC9I~r# zu5-No5oh$>jBS&LD!u`IDQ;*maeR;F>YERJn8`~0h(_N%*fx2v>HRPCjd6CE&2MWu z_8W$OPk`@Lo>&b`$KMYXb+3tGNV6^bid_> z22($xIoFJ$Z>&`)fX_H7{CK=PZnlZJHn8bEb{y($lt6Yi865?8|Gw~@6cvmO`bJ9X z4)$%DQ@DY?kTQn@Uyi#yN)-Ao;%n*vOkD>O3o^YO{ZY}Y~H+l@vq?#%*c%AYhNQX{+y`NVflR4bmQ-HWD`T&7rvD^E}Ot=AFjd+gF;_P=!Q{X zJjXv1D4%QuAm#Eb`Rpv2dcqwM}e9)|H!JI8}m1G_}m*Tl-VO=ZcpfWkEMVa_Y)KU(?R^<0#+&@JT_Fp2GDNa+Iu2`;Ep*UCZ9L4#H3lwV=7b!L>HY>I& zE>~QsxJq%Y;+2ZmDy~z!QSm0l4T^1ww<+GPc(>w4#rqT=P<%*nlj5U_k10N>__X44 ziklT*P;6J+rud@bONy^3zNYww;#-QmG0OQ@%u~!)EKnSzI7D%%VoI?{ag5@4Mc5#T z{!+y$ikwff+%iS^@(A10RW4VoP@JoHj-q_lf$aq<*D5YjY*cJkY*k#YxKeSI;#$Qk z6|YrXr+A~{O^O>7+Z1n8yj}5b#f^&hDL$b1km4pq-f!6+;%^7IO69eRS1R5Io#DX%K3@~ih~rz-wx6ZRXL?tq&P-#ykc6hRB?*paf*DtVENM&rz@5#Rw&L@ zJV$Z9;sV85#YKvZip`3xipv#ODy~vot9Yg2wTkN$Z&bWVaf4!;;%$nzE8eZRQSm;- z2NWMt+@$!ZBIjdlk0FXf6;p~uisE+&>Bg&^RxDMVqIjHQnc_6X>5Aow6^e5e&rzJO zxIj_-8KK-oDmN-NE4C^wS6r#MN^z~?m5SFYu2Z~G@g~I$ifxLwDc-Jlx8g>{`xGBg zd`NMV;-iX>DL$$AwBmD$n-yPBY**Z-_@d%VimxcXruc^9TZ(*8mFr9~PcdJyKyi@b z5XGU2Da9g1F26>7;}z42;vWk8>GzKD$0?R6PE(w&_#7Ulh8Drbfp{q9CwMI3I5h+h zE5w(Ghk4@vi2n7i%@6l)-Vca;uMv@N2p(|h7w<|U^1VVtK2kW8A5&~sSqs#Ch=lG6e0M2#i@#?DW0#mNO7&=HHv(9Ks~oB zepB&b#h)s2j?Z+Rik#C^&f~lTc%WjDqWIav{%Vz*6~)gU_RD+>D1P>UGS33uujyp| z1X;e)2X^Xy@v{e6<~u;~vj-GEdqDBC2NXYhK=HE&tciv76hC{A_?011{OkdrR$2V)L4Hx?*A@Gq&oI6C*#j1-EPnPNPgPky?t#2OW%08I`4W}I z&mQCrD&M8}prZKML%hr@fHHpo$~*y>hYrko3{ez6dyvJ?9&ozu7e9ND#m^p4{Oke6 z&mK_x>;c8k9#H)30e`LeUs4o5d)VKP<0er2>;cE9EPnPNi=RE9_}K&2YJ7|08pZXB zUsAkR@e##m6+0B;7;9LrjH5vDvj-GEdqDBC2kg{z;%5)C_}K%BpFLm!##8DMKYKv& zvj-GEd%!6ge}dvEisEk%@zs>kSL+oQYy4G;-&5>V%K0Do-S$KH^^w@gGoms>aVy`81X1seGQwRVp_senj{4`$_7(Smm`U zuT%Ui5#JWvtnv3KKBV|##pe}YRQwl_-y~q*Qk<##S1Vqp`>$8LP4_>d_!Hg#GsSmw|9-e{Qvbn<{A`rv;5OvN z4#IUvgj}ZbG?f>qT&wa*l~<{Z%WKDe>Kb&x7j;y(Ge31lPcEI3lCX0>b)~qsBE)$w z!xiiu=neD=jP2zn*j|>=cLRLV!6q^eTgkkF4PrA}0Ncyq@D0FR@%9Oww9TZ=r7vmL zNgCR9#@oNJjp=%AC~<9<#=NL8~OdG|+(U%YD$|`YarWxX} zY%@gfGLASrHS#VBGa=c=n?LOI$MLqv&l}>2fM#!r(CRRbUJR?luOTU{4!@2>eX9N0 zRKfQy#_BLWs&6Us#6zpY?=YqdtHUr?d@GC0>vuoZ#z!Z=1-)@j(qIJQCBIK;6l=%j z!Gj?bL#xB9aF2~2ItDSYIy?+Q5Lz8_4fuFpu{yj4n?r)J$pm9vtHU6&I{Xq88YwUN z5o9n@esYW>iPd3{i`C&>$W;_fOx})~1(DSuP3VHi>hKqgi>wYGWc7|5DaV+fO}0@>rq$u^pfb-7LaW0d!|E`wR)<5F(OMm*Q2$^j zR)_bXR_pv=Uf8mc)gfP>1d-KYBNatfhpbD`Bdf!H(3Tg}C7)r=$m+13TC=SVf60Qa z)!}G}^8>Lu3|yb3lH5|9WG%j8N0%G z;R098D_q02FtPcCmol%lIy{2yZLJR9&%D;^@G2&@R)^nULt3lD&ob6p9gb(LwK|-@ za;??jhM@5wpNF{G6Toz@B>usSRG!eaSRKx0dTVu4^HdtV-4hOOxj@4l?;~lHRb6A#Rb$AWcIaY_K zGTyN|JcaR&)!{tK-B}&hBSEfKhXJe(zrYGxtHUp{{GM7JqN^UnYZa$StPXGDLGLZA z!%whZjC&amK>6`FeE-3sFE|nqYuJW;hw$mm|09mVztJCw-3)liL_0yT*STKoaOMuB z#nAO{AcudZcgRPX=^|!olT6IO=?OCNi#C75m_86HLJb>4!x^GsgK8MIAKT4X{CFoq zig}~>zYpws_W6S6TzuWT&^(@)#m++$8=M_urh^_4ndx9#&2+GZZx%x{9VCvdZPxpK zdV2Yq;C=Or7RLJEs}W3vGrr9q#KanSg3MaOW~FPE_sqp}c`(j5v)g@~?VKi1{yBZ9y_?@?n-VjZ#@+R$aGf=9!!NTdFItMP z|M7_e*IccuTDl7h*=Buk9errcWjuHMb+s&T)!4vqSiAUodPko~@6s31yY(fr9ahjY zoQ|>Ei_4ltWy4Q+cjpV}@Y9p*kDt5w+IhP#o3U~*zd4o>qr0VHceEZXtS5T*`LZ19 zdt-6e-CD(Uk9>IyBWm0caAz0(h&P8){M;LbxqW1Rdt8bagnUQH`xWyu9p)d~eS3T* z_PK#+8F)WqdfY%Ibd^qKJDP^Tbo88Hg4vvk9cG(x|A4?dr3ogE-`+X9y>cAH7*w9e zfoV~69CNU3@?g`uG>Px@oE>J9JejMn6?a}97xfWMyHsP_{JG zH=5bs$d1u?0^7M1Kc+3hFZ`y{-a5liIqgK|!g`J4$CA$HknSWn1wBJi3ARBD{J=Do zd5!OsCr3vHHR8p9V-;CH%EFTar>cCK;`xe;6xS;9k(l{}CkNiHvhd`PA68j-a>&Ay z13Pv9pB3}ap{VCTMd8UI3r`Ldo*XDVIZ$|Vpz!2C;mLuwYySHcg(t^;;mLu*lLLh( z2MSLP6rLO?JULK!a-i_!K;g-O!jl7qCkF~o4iugoC_Fh(cygfd<@$$`R?1BE9C3QrCco*XDVIZ$|Vpz!2C;mLu*lLLh(2MSLP z6rLO?JULK!a-i_!K;g-O!jl7qCkF~o4iugoC_Fh(cygfd<@$$`R?1BE9C3QrCco*XDVIZ$|Vpz!2C;mLu*lLLh(2MSLP6rLO? zJULK!a-i_!K;g-Of5T0e{o|jC@52C2c@Pl;{t!iab>aRQD$iE@nC>s(^?~%fuf=fh z=Uj)fj3x;68XD z3C^Iup*a8Zd?&v!_K(D$;XCPMO8L$K=%wI0A3~=C-}x0p7{2qXjOp8l)5JLV&Lfat z`Ob$J(*@rd=86x8B8Tt%ABdLkyr0sjx$NiUJLggi`OZr)zl-?JLm(*M$$N|PomXR% zd?$zFF8I!y7^UoIUUDTe7%4wF*^zSRJMTm;@}2jh&X(`|6U&MC&aI4#_|9*#@)6(J zj>fZmCtulGzVlIPiTKXrC`EiH9a3iToqt6!JLfz1V@At&j^k19gzx+VY9)N9v~0w8 zu3&{CzLPJ7E#Jwy^o;L31=`4WzQCLj-zk)IHsARZ7Hs*>N{Bt+J2|_teCO$Gj-2?; zcUZBA?<5s(`OYhN-Xgy9yNrwY&PQ0@F7TZ{XL%9dc?Ij)1>d=qF}MCkTD0o^VdlB?&3S!P$S_xf6ha`QG9-}w^z*!FzqY#xrocb*S5!gtC5VEN9Msm}79p|7db{h6@8s(jm+zd)^p@}B@}0kAg)QHC zE6d*%zLVFg;XAM6LGLZTQ(TlAzVit*-wycBuc89}$NZ6qW#CNP@SV#YzH@m7-^u4} z{|d>(44j@AzH^Pscdl{x&NYVb9Kv>s_|7s&F>fUQ-@|;Tg5^7<%7*R~;lGgW)CWVpx-zE5p>wI8d${^< zB0l#G*_q*xYBxE~-Q)etDNguWI;ylPbaDoNO`)Hr9#fn; zM1+)jFV#tcdiR|y;ZCxvTxS|D|M>oo?*cI^h&Zl8RmS8=!j*~!n3iE2es~?}?JS+l zbeKT{_3cTnb2<(pSFUrKsXXdnHc!JgUXMnh+2Im-e3-Y%aaIFO9wyF}>s$_fDWqpR zlaVs|_!7(2N3L@M_L*SfvboNiociS>vC)^!bxwr7lu-sW`qp6EJ>E9xE5JV153i0R zec4>+8PNA63)4V-{N1XkyS-uHr=}ReiMtcq=qt@8Y97+%!gaPFT`pW_TaH}kE8sfG zmz!YP#mENT{riIJ#Orq__;N1zUOo)I_&?&cWMs2N+#6h{VLw?Wxl5Q&MS=b0@JI^# z&t%9(9_Fx_Hv!QTqm9xF);UXooELy(y_xX$;pdR=gxtV_?h&OB(_ z-nMgxT<1C#Y`M;n5PQINX4-b{&vwj->wJxe6mgx;^K3-6ohMR?xXx9q-)?iAKW2Fm z*Exgr?1JmOjpapLXQpjuCfCW&8XT_kHHbNIowq>$yNm0rMva8)l#drI*Lf&g$*`I6 z!ojY%9oNZE-n!;G|HfEr+gV1@a-ICV&fz*M7~3V+*}ZKi7oh8!>&&w4%;GwEWp>AP z9>oUexosy`&~v%Y5l|ysrwjmHa-Dx+ZF1o{`ALhzb-thZtZnCJ#&%=dnay>^d61Ut z*N=w4%a!J1$D!9c2b$cb)Lh4*x@=!u(@34KDX^$&$F@z zxK3WHhU=6sOZFDmDJ~=p*V%<_=lwV+EK0~bECb&P7_KwraGj|Pu9FT1{1VB;40FRw zV%s^+U<36mfb%w{&%+30YIdxGnvooEV$@}29R;5u(~>bEDjPTGl*yJG$RuW+5m;aE*D z{lLfu-Tl2>=k{DDzlkt`{bwS6d`Kz6FXTF-Az!)9Q0A%^c1o3D)EVtgK}Rpo*~@eG z@|<{R@8vmrY}2{hJZJcohB4{Pf#;k&DOa9z3`jbJoPF5#+I0Rm^PGH_>C^F{;W>T0 zlvJLRUMOJG`8Hx<)A>b27@qSL##o!q3!p@8I{(d>E^Io(T=9dT$l*EffM|KnuTdJs z7l~ric{;_A=Uju=#u3k%;ydKfhb>(Ls!b=oKhmaCJS6Uz=UfkkhRe)L(&Mg?@{_HO zlsnJ)C*-0{=igCh%X9vYe_AIxnR3%=hK`I>(Do zKt*%lIY~UdyLiseqejAWKEtNAJm(^|lI1zYL9r3@3dOqCi1~$0%xi5rC-MARAGSwQ zmE}3bfZ6h#FR>x5P3KiqWqD3IA8~9tSMZEjo6cuhko95vIul!)PC7VoY&zF4ueIr< zI}^vIlUJt8b6!o=jt|=vJP3#9oK9_yO($m}JFw}T&cktRI!O)+&nW|dco>TnnS|7G&QMtA0{4x_; zp7U8YfVJuTE@Q1t=b_ANZ90Xq>yqdEJ=@;ebT(0y!*kB0F2|;mAHcgj=WVQs!*gz? zI>(3Ydd538o$DFz_^|yVv^qr)=*zmb0d*>4FCMCEebS z?QSs^rjakKstkFms@A&Y4XtTd_th+IZNd_;kvS(_!@@zV?htpg@)x-#fp|J$-n`+)nIzoP zu%y1pl^YwD)VXr~(yGN7JDQhOFTpOTv`VTl1TW;SJxFoxsx4>HdxYOSt_S=k{nMCW zHnaK7T8BT}6Z|Im(i95Kh2NxotO=%m*|wb5I`!+0-z>?|mXmyGF8t={(DyhV8BAdP z_`6ln>>rY&EoViJwwz0HwB@`hM_bMpVas`}sccr=o!xS5M&1|vCb@4Ld=Jf?;kTCY zq32?mx)z7owA1;!OcQrLf(jz8`X`AsuHRC+U-XI)q?o4k9ubFAo| z7G5vC{AMq|*~@R@IkA`D>@mMdyN=zm<7_cLw?m3^r*@o2P0873?Q%{8@b0)&AY7v} zg*7_QN#D}xwXg47fOR)x-*4~mD%!Si9nL|UuIu!<4kxg`*S5vm)ZVfB<+hIf3fi_5 zR9x5jQ2SF`Jm7aXJ+`T`v^hkZ$A0}se{=wKWR zULjJK1tsCxx9>=Hx(fFj1_~-JbZ?l|; zQsv9`ctoi_%<4sy>Z`2(QDMG<cwcFGrN>?bMPY)X}DLB=hm%GLVj$7Na2xVtQ9++7wlen;5GdC9S?Xhf-!EsAGQs_|%9 z(Cb+EJz>5f$vE>xlzUY6 zs&R^zQoWU;rBumW$E};(53pQIseYOTSxR*P^IA%EIP+RJxu-I(rBrDi7q@P5d1c0( zWkF|CwL_`WxpLg0R7o_&9ZHqntmE6e$z8xIIh1NM)a1vpET{~vmQp>5#al}C1ZL}o zQYC5KKaOQV`?6Y=QvD>=SW0y)W4obLNlqF{HNGe&&XD7lQXRxt2BB{+m7l5rPk0?ya-;-qIFAg`lEGAv5gebYNfh{DfVJQit~(~b+~_2 zuzY4|@X_Lm^!W6Iag)YPC`p%;EDHLq=FZ~M@(JTg!d2{njUQ0)T9Y*~8UKT6w z55dH7e?=HKz5k+5_`DNLfGkz;+R^R$<9DGsOpkrMG7`ICh}k<$=w^jeS(P)=2b;qs zaDPl{vvlEU46NQy<=E?gOUteNQ;xSzwezbF=j61D_ zmlO>!j)B}J)9)-DIV6(@n;!LD?Cda`Q?t_XnSggp6HGe#ZFP2gH{v+BTndkaYn7Sf zn1gMT2b@b_zeDR~`PdqN_BN~0x*fx2v>Ae9$Z#p+LFdf5(@N?_;B=n`Q zkNU{@8GS9-cJ-Bk7obg{2_`O^FMi3XUw3@*N1!idlmU&tHQ09R_ZsvSU?1y;EK&XV z1k5}ZYt#oQ8$D9rG z`XX=4cHmm48&u|X-n-VR=iQG^NWcdgT~oqE?g4cp@di!77XSO8u*rTjH@`Y4awHTmiU^ibyl@X#ptk4)do8}A}-9P}q6e0cth zf&2Rh_+^+M4o!IB&X6V^2GM1Wi?N353HjS+8v#1mNtfD=U4Da?0?(C4j?D8m@(RXCDLC7xu5HUd)>~feZ z{tAm!b~zZGTm`*B#4cY+Y1CX4DF(ZTQ4HDTn=$ze47>b(2(ge|enIN8A07VPh~JRd z*yOXw6?56;Si~-Wm{BU_B`-$?BjqP2I+C!@vC5*iP8xZ=+W0{Mfv(Wg~WZ7BxleGO4>*#4fWgu^zF@L!d1$R+oI9IU{zN z9{|L%+2uACY}sXguP{F*>~hRymt!uw9J^yA6_3tKj-twlU8Y@KEQ4K+MeOqB%n`B6 z|E3hN%YWpVirD3IDMjou9Vf>kcKIomm%%Q_GT7x<2D==K*yZO~Uc@ech4su}mtzsT z{B4%^czAr}$>}UFVwYc~6tT-Eu%4D(CP$hByG%c_?=E(k9~5};*i(gHRmrrIs%Py05 zjdjB=^G;^ixG!ZE?2Q84!e9K)j8}k-)_VlcKLM1JM1#w zJjJ?Wmz$6vS9UoDcA37nW0qaMi{ zfnv%c-uTZ{&3AkLWKm7EoUcK(Xe$wI^FnQ*5Naz4DQPctAWv1qJl{&myMX`ufO&3N zT2r#5?xMzq>IoBzCzduZty8fCyJ|{Y#GOJ`ldcvd$B~=s0r9nOi4XaCLEL=Dt7WaHP-|o7yJpU%J zZ^5ib0=JjrH?9AmPxx2It_UuVUzV8Oe>vafGpxdo&$ZL~=YPV#Ja!qXCGYA{ZG^Yb zwb=K`IvcAE_GBYUA{G@UmjjV~=6QjF%RomNVI|wzbWx2l67Hp*_nd*TX=9STLh*yx z&ykGojzoSp@Wypb@QYkkJJIVUkiiXOgxQk>vT+npw{+=}Ca-2u75Yzg;{{A$)U&&lsv|QBLK&a#-Yhl?&@IZnoA{qHg$RVcF89 z7;-CXuxDY@(iL9ivL(w}>T2b~6@13hKvo?Jo0fq!7qfm!to)u>mA2PI^By!`-r-ub z?#;v8QnLBz{DVDj3~nxi@WV_c;vuv7=xvB|1Jg2O^U+5*_DXRad~)P*@I5j%Wp0?wN3X#R{>w%Q(wNQDu=An za~4y-E3j?yP{+N{m*R#76SoGktM5_hE5JV14_P98*?jb~h}>ol66vUqzgran621xE zFc&`hWu!yVQ84w(=A-vR#rQ_l1e1;!PMX4)DVUJ0!cIOTLbV(RkBi63IhhF$fR8@I z2tcFH*(ICq{(ZqmoB1dETnx41aoFI?w<9Z~9bx<`N9KI=Zbyzbv9UM!YK|wY>vH_a zKhr0V32gUbBLFEMhu<2OR=h+} zUdti<=TyF3@qR_&w-GP=Hcr|u;THGrz*}|ja$z&d(-h|@%F96PU!w9##rqT=R{V{k*hM4#n=0pFT%+E> zipMKHttc<45dWgeuPX|_js1n_JIr6CI9YM3;u(qy6q^(;QM^uZgW_F^4=O&cxLNT< z#Wxi5(1BV05XBP36BMT_o~>A~DC^rI-zQbRS@9mlrxbs!_>$tk6#L;?VY$L@1IMU* zwBjj>^Au|pTNKwQu2=k$;=PKGC_by$p%}-&!*WX%Pf}c|c%@>e;@=eisVMw5@(;w# zfqI4$(FaEoQ){)bfFr1%%zzns?*^vU>v4~=B}fLz4u7V;P(`p$@0<@$Pf1Bw(C^Zd%TMurR7iu5(yioqn^{TRE;*L5W(VT# zNjqn+?2N6na}K~iuLcKB{(Cw&YJbzTt?^B5et~ygr-vWyt7!x6dFwmLfBWFA$$dw* z&)(b)*i;vKDSUyJdZdsx)yDprC5C0qk+P^e0M||ZER$Tj;oTR(F58r4?5%Stqn9h+ zMXr2Dv^UvuhxR60E{wXhv36p~kux?i0~Gpkytf9J0HfO@grxYBvh8{G*y+n4ttQuW zU8yG5`=hgh6#p)gf)roI5qk%uc%M;yk3pV3mK5*PJtc7R!l9@okKYNbzS_y@(Y5A2dfIBE{!ZI(noW|Dfaz)Dn^6{U}AG z_&2E~lN5gy+4B6vNy!w;%OJ%QmJ}~xMoWs*<3wU7r1&>ct95>2Uf8k`DSj#|baq(T zc(R3xB2t`nN%V*m9|UcAiMr%(m@^{9@r>yuvPtm`EZCCb^u;_sA*6W1CB+jiDW13^ ztY}{HFjh1o#ko{km8Am6yMC!BU1bh)-!_?Pei2nMwa(@czor_a@I406i-B?_%znjlHzd1WRX zQhX*=JEZuhc@PdM{yu7RNb$cg{r05zAv_$16u%T|@)ID%WoXSL#S@kkf0MQ8h7{*p zmi`Hl;z?G^lH#WDB z|6hK#7u@P!eSTMVjE1cBMwH1iHIM+ z2U5&i%>RABkMDDKd)N3Im#4?Ou;_u$m}W=D))4`Yt{|jOw`STqFwOp{=ZgvUq|?UF zm{mTZWJU??vXf$$&4|+RFwCAfzI?(g>=)zgeDo5RE9dj93G6{)o4qy$8|?5|mvP*` z06#a5BQI)=&=*ev2ch5C1NC?b;7y-7aN46F48_O`Cz|qh6}I;A+v6GbxX4Mvg05R-t*4A zahKSUe{c5&Vs0zh{CIng_SzXQQ&ApfH4?J<@hC#$O}aE>vn^2&$j_KyHm72T*=F3e zxGVB5YJ!P79y@R+jlvr^j_Ei^zUJloWgai4k6t!EzCPdczGjpl-fWV~a`lygv}Jzk zBN~0x*fx2viTi$T_S(~-FNO5f$HOuD_?+PCyB7K;W1k5oE}I`;D`C*lX(}paZHW0*-=od~KGHy#EAE+pt zHuejr4HQlrD4aG>IBlSC+Cbs7fwyb^`xS-L#{OQL?XZ1?(?)v;rwx1^V;9eJug!MY z4)1oG?N{=`dj4C{*lZ8L&5P$zY_@?%P{#FMtTiXY|mgZ#Wd z({@|N4ajMgWxRka;|=}{n(+qnab2iAM_=GaA9GZ$-1XC}6mH3^LoauoOdgN(lQ=M9 zU2!r7!+trLq|q~Gi~B`vEr(ge%)tHPeIvaY14|IyZ=jPlY12-@tSoc%3U9d7tP9D565nB*FQ$*1b6*I zL>TUx@3f3TH@NFnynsUPdOvPJ&19;z+H20X}RnF z$9i_bUHgoCd?eDxMwchgWj!PAnijy8yS|2!<*v^_MRVY;-$Jr?7kB+V)JV8%dP;Y= z>(y)t%U!p+VqW1jY#9@qU$~xmyX3A$`c- z`+&RtE$SX|*T2MpgS-9=_I1Twql*4meZGiRK2a^+mE8(vjw-Lr>Q){cAi|7I$D=eFU!v^DsF z_dIRAtDN<7?-9=Wr5cHs4O6iOXV7_ZAC4!0WHCJz(m%2^)=eJP~p z>*t+u))SG&1XETvXFcDkAKsKk^~>h0zk~PrDWeQ%^sT|Rd%U&K$M^57AF@RHvN`Kt zBeDa}6DCj}f43@{{hRQzmi(m&rrbNR1M~i7v;PgGL(x$%>9RTN15vSDIO{Ty4Y_dE z>(F1OMy1+zadye3sSkyH!CAx9&k3QA@kP;|(8oBRV#46oZtrl`I6V?rCOK$wzb3H1 zj5Pv~@{#y4?E?G^XC2LEBFb7P!SFF2&405Nrq&07UcMUFTQ6UY=P0y;*kA+21{)|g z*ucRUj~FjD*g&zt2KMsR+HN3N**;=}4HO$}U@u=Cw#y0(qr1mf>j;;t@A0FK&Y7=n zGC{wX1u%X~zq>V>vx6)4O=?Yge!5D^~e(OUY;-(=#QbNrQllej9xcWb_vi zVb-qx6Jyl7IM%MFHMXu@{RU#XTDv;T6+Z}y3>l5JtM7nl*RK8=B}+zgx#mPjMt_2@ zu@4=C%H+p`aS(Lv>i#HP*RK8{3g_C@PdBD8VsYWW5tgN)v8?P{)Y?yOyX9>gB3U0u(rMPzg(n^Qe)mUHuFX-5OH2u$2tm951YM#k|7r zvSmzceqj^yS~5CKmDZ5DjCm~?y@H7?8BKQ(&f3*iG1ijNA7ZQ}qxngkvv&2fEXb14 zuQRbFqx(>_Wb|6*wPbW7V=WoYE7M)O`f93n45`=hARID!2DLec)WwvyC!?qHa2zsv zG1SP~)iMBBGI|_~w`BC;%+`$|HAin*yZT>DXbq_^q8dv^AI3^|V@N#-$qgCJwX13J z?yOxc3u9X{`V1lyEm(O;z8ogwwTNRTTT&9$rPQ^i@k z`qwPKr(`tOuI9CB)~@C!WqWPy>K~xOG2>l4AJqi|eKGdw+SRLZ{O;P-!%!E0@2p+T z^;-7w+STXrbVMxk9gy~&2l4+-3+d=Z6xyn1Hql5OSyS)8KDx(CSMRa4tH-fR;}@>P z$!`-bY|*!Cx^#8)hK-4KWE=eeBQ)e{8@&Ug=-s0{qf>KXRb@!yg=}BCmz0K$ddpHe zZf`N>w~MRk(b7mclhS*KOkUKqv^ABvj5XKbu4=3li{h%5n%0KJb)m4du4P#xjf$}z zHf?P$f_vzunhUTBGk5no7~k^_#=AG9hVz7Ic-T3UY!dhS36K3>pptLslp8C4ul*{n0zXWMaFlA+P z%G;d!;gF*GWpm0+(3djGfJWaMY`e$Hb+E|^vVO=C>C5JnKZ(faOlLwm>f`TLMSz4F zkdaf205s)hbIPAbx?DKrZy{YSobtB8xpB&+=r1SipnlFS*);X0urD}eo=Y2iZvcea zXooLz(M66NZDM0@*g?beSOUu=N6hiV1ooH1jR2%P89%12z)v~lXu4=9^g{9CjUjg?W$u7zFvS5|Jg>A6P-$8^~v-)nv*fp#9mS5Mb z{uW}oTC+OL6)%M%!zyFV>PI2kHLIVbG-@u26ccAIF|BJ>e;yOhXw7OmaMd-de=2nm z6Xz?i$u+CxJ0sV`*{n+)PyP=S8YwR+ACyF_avIr9T<&XDe+9X?X7xtaIbxONlc$JP z{w?Do6Xz#c`N+iiA8h=HRlbc<#410}+D;iMtuZ7iOJYQ<^4F;)lU4o;63Uv@2QxKr zz`T}K{t**fR`~>qmQ}ulnk}o$E7M)G`U6z$u*%o-ARJaXPHhgW{0FAro>i`4l^j-? z45_SHEdzjMm0zMd%PP~~d3S48k3wm(W;NX*J8M?+TN-E0YVxh!ty%pxOYJ-O)xP+I zlxtS=!w_f9YO;mSn$^Ro+_K8wVXS49KS^DdRsK0+EvvkRv6fZ7n8%mNDm!adf01oz zS>-FJ%3+oFXFVJf=Q74Stnvbu<*>@vP@TgnpU!xPRX&CB4y!zma(Aq9Jrd-~Ds#>1 zmsw$J;(Rm9?txr%JkE%YgY4g{-D_gx_0L6Z<>;P!I-da`EqSfod1M{w4xmLrDQM!00PNRnhn%rob< zCUnC*|A!Z<*0V?=y0vL>wV_>UmfBKRgEhyOEKJECKcFS`Sy!71ZA_O516p0xg0*p( zd{NUfyo#)BsrM?ImQ>a)Z)mL)PvidqOJ?Kg{6D!!HD1b^-qNzmEOHkY`%Z=n;|tnc zpWAWK(Rz}-r1YLArH$c!w@$uy&PVrJHvgZXp?AnbySEO^{Ul`b&~d!47=swT3dIdL z;vWxH8B;O|+Y}8jEkiaB-Br3Y(waQX$8>vgz3TBejtU$kkE1&tdJS&ww>cFto2Mb4 z%ge3=Im-Jz^%0?3uo`IcU^D0Ss=59(+kpDIVq2I?@{P{(q5*Q!>(-e}-U(4o0D@3o4;YeN=Z8z{Us zP4;;^YOkVgi(l5Z1=a31C4B?-pV@&Id;K0tKeuH_>P9RJKCmM%wYC#}cKQ`SWq z=;syO*f}VDBZiRbcI4UePDNttNGw&5$9?IOw&tbRcKQ|H-xj7>wKe$v?0pM-RmHXb zew>pWazco}K|~%W1S%*b2Z(@3H3}#qV&tKSNJt<7DkL!pBG#)YsMM^I9N2^vXwzaiZEfCULwY~QCd0YSG|9vxS_MUTck{lkQw#@Ih_nI}2HIF@e z_P1xv!>QWg6mRN?itGYs0nWN^>*uC?Bt4It4`UnSwjKZ6z56YozIJ>Q2Sve0a0Yg& z^3gYj?k)#J3H>pbYmmxMmVCUnd-t<;SU{UW%4?|#!08Q+H;YX{nYttha^A#QPA zU))C)^^5xeMBWH&0j~E6_hF4`YecpTSDSYq+&>)Kl;?8xbx&)wXYd8n?G>*WKJM!7 zINW>H{WzM2M|dB}_w`ck$USh+%U7=(m57n3_Z1K*)n_9tl7!<0q1vq`Cg_ zRg^MWa|7e-LHuJhH#q(ulrlwgNvC);JYPpLG1s3KC%p92wCR3$*fqCU_CswtR|5Mz zXw$EQ@atWFfv(v=n?971&eBLpiO69{5?AC`Yj|9^XuZzFlL}l=Ync~g`^?c z^hgU6Zk&REy$7&afL-jX!>)b%uxo#d#4D;^=lEaIOe}r~MF!gRNvy{-ZMq*EcDnyMC7GJ)`5zj#K~YKdZT_`0M0?HQH43`hhmx`#D&nO=pCD&+*18=|>jz z{ZZJxryxIFn;r({;j1R_$6)Wz7=0qMaPRvm+}!7phL)>rB6ltQxzB(bh(fGS!y)z^ z+&F2X*GAktC-y^|$Nvydg`I&M;kiGb_%4e+$)gaj)JdF*u)fikM-t3Sz{V1U7Xe$C z;BFEAaT2#UL2bGpYSRZY?g`?XN|Z8is7?P2D{{K{PEJgqZ>UW_l)j-heI(PJC4r|W zE@48UHvJ9;4z=lzG4SaUxGFJ;Y^Y7=l6ybYrt{78t;4RVG4w5M`eGKs(x(53{+2eK zQ&;@X+VuaWSWBDEgP99`9Cod`)=-u%ru)#QAIU6Jh_+rS%)9c znTb2-8*0;kLEliDUP#I5+H^lW?D|I5WvET(%5mS)rgIK}Z)wwc;J06^Qua&R+{8lq zTiW!E6lZDEPo}@6O+SwQmNuQ^GJZGObZ-d)<3*s ziV+qJu5W@}1)V>n!EujJ&Q(b3EhI2Bitv7*n*J|vo^uZG9~jlNvv-=R_eQbpsDCcA z+xDtP90-l)VN}#;3e~Oao9bHhInlx5XS4!Y{EQGln23T}AL&l0DLWmfL65~D(0m{caTT&uZS}GXT9q2RvA({g4r_ON zSdIKaP%Q7gHo21?c)zKC8|p4>h2FoL&8+`*x3J#3o=@xOrK9R$9dk=v9Y)bv$2ilu z6|3dXt6p8x0;aw}bz}#ZEdP5fS?S06ms zxwo)w44bJd?kZdgkxd-RVYpn@Ec367Up-@t4B|`6*(rqXkBbCY4nj1R7az(_YnBHT zIu5stF~(Tuz`>T+-&zw)IYwSB>?RInY{fXRtq(KxOgL-H8*Hr!?oLv>ev&>p<^+5_vw!wb*% z-GgF*??IR6H{-ox-l?}4E;J>S?SSql3Oz;Wxk6V8-6ZsrLT?qiMJWI9DCcpZ99Sj) ziqHQ#@C`yQ7W#nD$A!Kwlyk}{SM7%lsuqWVs>KwbYCmjHwSE~??S~Ee2Kp_=`>Rl; z=LMfY+oAgip%a9jDD(`WONBNHy+r7>LO(C`9-$8leMacZLf;VjH=!{!V5UD%Xt~hi zh0YXume6{kR|@@<&@TwRPw4YP)%s<~SL>HS)%s;nwI4R9(({6j!U7QHW0KI5gf0-e zTxg5XjY4k{`X!;?5c-7Bp9pOi8bK$=bSi{S6MC`G%|fAkRN>wh`mWG?bmr8v#)TeB z3VAEpuHjA$;Nc_7e`@lmhV1&+WdO`;Y6BH)kNVf6bJxF)VJ7hTsZd0(L#F=Kan5LW zD%*DSr|xxYTYF)$&Fx*>hTSFG4)LLjrT)`(n%d#F1Nu?_>SUW=2tOxvMbt@diuSkk zp-UkznSwrdJM_8K<1U3BH`TVI2s+Maaodi(WZRCV(49i&o6JU59j7QZeOs{^33Z?N#K+FwYsq&1_H(lka zJ95T8Wu80|MWemkY4FCab?$@Hx9ZQH zM6Q&|Q`Kys^4!P_ouxAriE}kosPbf9_Dtnj1!+`ydW;#UJby=SMTu%2!2)7#lkA5J zh^ZOQuJV+4TIH#kSbP>m1}e|9Ssh)dJim%${rhyhf$@=y7pOdkGY^5v^T&K{fy(n~ z`UNV_r>RJC9^RC#hKXjdvvA{74_9dCAg9rGD1Ag*KTfy(o1zkRGwd;Z>aKI;(`qq5EHmtD$hK|4ON~eF>bhkxRG%~mFKHtEO=Xp#%TtH03-JQyFG;*g@o=T}1 zsyx3%F`>#c$xL^x^2AohcqFobm@3cN3?H76TtGHddEP+bp~~}a`i3gcBKn34h^bz; zRGv%e8>&3dXZgBRdH#@fAF4cgKDTeFJk{J5OXWG1{?-EGTlgp}mFFWAXQ@0drN5=} zTt|OP<#{FfZd9I;&mzE{s65+Q_)z7^h1fl*JWoRsom8HDSB=W^vn=%9sXR*{8(l=# zDo=kn0(dVm57FyUS>6nncAt||@&;esYqombq8 zYFBsu;1O(<Q8O1xhGgRt)0!A*2mG8PQsPk`Hpd2<&Jr2@w-zKB@s2Ilo2 z2p_u|Y@pcf@CKbYCJP79ft%%4eU)U zr&G=Qe$&9NURB$b67@i@?JaLv-_liHdRTfW3SEo{$#0j@!Uh{mccF$2Rj=4Rx274o zSv)H=DKr<qQ(_@AcQ!*~Wa} z(6?%3>+GT={~KxOp2DU4uJo?JYopG6QwcjSquKPv6&xPi?674?=@1lyRJe3tm1E_$ zLEZJOXVomViq%l(G}kmNubAvq*R82*UQySuY`t^xoCV2g$q8i@WfjR$&2_8mu-bNX za@4fO<#qG0+;&-A^ZMiob5Bc-Po<7Nf5O;tqs=+8|Aso<=DOAkYF0Zvs&rNRVs)o& zG`-haJiNE%VPnS#=S5f#{V%g?HT2=F*sGk?C52QcX*aKGl~v#C>uRv-``=mr`!B1! zHJzMR>o2cauX`x1h;~k%Hf>6B6gsHUd(=Oj!14K-x|-Tmm}doR6y4XP`O{B3#mW2k zP~v7g%XowSQmDK6h-B(;2SWGBZx)9f0X)>_ksxZM@ceGkJfKN%XIPp{9d1|Q_$@SX z7?0toi!jDmPlSW9)2|ZWKz=QaF@Bjk+!V^eo#esK_hamMCM{x2J!k50n;^y@8AExz?htC+ z564?L*)W#h-LRugH5spY2$xM~ow?rke1ywIhx_?M(PogL1QK9*m{n4i)wE?;ombK5 zr_dsXWyof1_XDBBrOq}qu6h<12M%^_SRS^d(D+J=Y4d!Nq48Bs*gR`FG(NPp#-$3* z)Zr3_8pC@02rl}iaM8aE)EN333|qF$5w)%Tf!CYCG@C$9n#$H0f#}rPUI;n_!dPw( z&zII*TZ|IqkRV-SiY}m zI$H4k&}q|sC@FBjaG__5`}IOUC+@ckeOBDLZj15wO|x~n?7PUT{sI1~f5FXA_G65P z(}R}Pv<52P9@p58pE$lE*EP2Np}&U-bi4`lrcUbm4z6c)pnFB~1(DR(760z;-GiLe z4V;ScW+Zj_u1M-@%Kab8y$x$&FYn+Q*KT#Rp<33gLxn;SK)I|79VI3>*jmW^bfsjh zj}uqxSbKfMp%~{iT*xzw)=5?>yW^|?b@`bxI!OYfNYt+aiADYIlFK*bKf$>%zg;`x zj&9$7T|06>*30+53=+$ayi0C|I}s$xsWR@Lc{|yqou(3(dv0FKILDo?{HyX>#>4&+ z-OtF!vWS_=d2assj(dt&n;mzS4uEMP4G0;F{1W$Gv^vk@@|>^pzP|?NnpZWf=xRW0 zF9zpKd+jtfhB{J@#(y5gPF?w(*=XDt*6Os5fH=~aN>~7?<(ck)?IK+IjesTl_*A9x z(KkJxoQmkZ9Rp$YU<^dPgPB{#1$)Qs`A}AH?3UVZF5HPOaA%&=m@HG{!?p zgZ~%!6a}UUOdB}%mr-0S2lGWdYU04^ya?t+TErN?S>SDX#pqWVpYkw0Dv-x6*2IC; z;YQ|3TEv+8TLj*gHxTmp?V~)poBTDxZp(WT^6m?UyNN>~ z{MaRF5o7!=1#insS@{Dj2;|)eJL9Bq(eN~O{S^|PZn$m}s$KC6kfCh7?{O;XmIQg(gzpoh4s#|+VU2|fnkg>@(gdN z?cVc0s+|+2XQxP>xe1N0TB^hsJmK}HfyzpeZ^*~1pWt$;CNhsfK@-u|a7CKKT-x>PX2wf?Z z?+D#D2>m1}s`^tx9}~*w>7j}+{Gj{|<}{S0H`sG!^B!Q&qbE(w6?^W|k2nK+V$Ps* z6EJ4g4zvioX;tl!)W)6v2aHK<>0@5t`;9T5V%^{}Z}%u=$J|p;_q(_mjLD88k{`L9 zPBUD3`@`rI_4&#IcIp5=A13hTz4YTI@(3PP2osLikjwY^d}I01Z-FQn>&|d#r%}lQ zV><4MkAiip@_@WfET>f=%#)PsJfKLr#UfAO-iub}Ib0tCEQ$8lHJ1E=NFH7(>Tx{B&Q2NniV#%(;jX_uwhw&IL7cBWQ!to6;#>kig2YfaHQwgRG zTu)f?4hYy5#0v8?3vRZ&?O|p*yG#z;}_24uRARHBIM0^g z_|Q;g4_5@2#oSZ9`d{uV<=~xoumyy&ByBz1n4({ED`W&S2Wb*Qzz>u!(_qW}< zG0u5NhvO{U{+{DYGiVu;K})`1c)wxvE6xW;Q-UiEP!g>JJ)+Mbdc2Taz9IZG0A9RP zJK|0ddAoMJhFrd11A;+Im7EM(ew6{P;O#V*$IU-h&u2#E|R*0t~4-Siz9^B<(AP{2-1;Hm+VB zfXJTKJCC0@F6aHGAJH8^fFTEI7hbMw(Ef7L*gyGyD<9oB@Cn~ z!R6kDn;CG-0Uz!{@Nkgv7D|Zu1b`tv{3kiVhxanTUwA7E@F7zHKCE%vBb9emUaheb z9a1-~(c!bOdCoU@{~+tC*`B(FhhyDS%d_uyXZJy@lj%i~=|!>Y+(D|?xH%8ux(FBX z7E+;8)&&)eZOjJ(Aa;ce_Zk1`x>nG)lg64gWX3QLJVc~A`NOZa9)@N_oWOpcrUIAU zx^x%kIGOXWspI44mu;Hy8IJr&;SH9Y4c;~elN<#{hVLm{3Smqf%Ave$Sn|Y-aNh>c zx5*eIgP0Rp56p5fZQxiA)`2O0uMqNx)tEn619>g5+wxXIUN&q+%xTLTY~`=el6O^xyelCu2{&U* zS{vbD%Nt?kuLypoyq|%carpdb?t;8LBLqCHyzUSRqRhQrV9C4T3$G>#=IjEKt-P7K zZRmNj{!=h*WGOHlk1U2G=U?~1#d5ITSym(!n5l5chS`GvVxfFvde&{*DKI_L8nlGY zx@_~d8vj^^w%Sge^4M}hvp?or^I6lduOOBjpg4VFy_ruKK9mA3b#HCPJ@{@6M!*j` z-rS0OQm@VTFIW9{)A|5z<}mj%mCAL@@?G650=%i`UUDAgh7QiNG;=V~nlLu`!JKc& z|8f1^X=qvjo~8tsdkt>cp(%f1fkK7whZ`vLP3?#~hb~ckI;sSW{yS~H<&zAcMm?vw zw9_zo8M>@g{(8P;<+F5G+VJVhS7= zs?@IVZ|B6{KfwCo2dij0+2&Z{W^8)A&{n*s+LM=b=?2DhHZG#`e57mYAbBX1V$wmgpU@f%Bd-RZ?S z2AM<{V@&x0rZ20KB40-k((C)@GIR=?TTw_dmGsiUTIiTH* zY5o@Sav9V79U9JLlPIW3_b%As)nvT530N*TbQ;1vXd+qiC>L1@%yd>N9Q%WGheN01 zAux;r-^P1Pj}?8HamgN`vDL*DlkW41Ik z1R%4=G0jO8Ij`?&imGRwhB1q|hof2Jv%PHx-me0ul6Z4|y9=E;>bJnziZwvMpuV$s z2SfyGfc}non31jbBkbb#|K++Lr(t`g+qR=v&bQmcwLe+TpzCTOP=5MCpji4spk4)f zcS3dcRDn^j$3?-ulTSofgnEG%E4%(u42n)eFq?hU+6a~nWtm=pKhd`#Y~RNhPSc~FBe zXT-2TXu5K*ieZ7!3}v0$iytd@{%poCh-}9_u5cQCKLo?AZ0cRZ*2ghzzNpM^o`w6& z(G!->Uiq<8WpXDmtBqmp{*v{m246RMoa7--``55tfs1Go$-{MEDv`iEdvHm-S( z^z_RcvC<`!od#ml7?b%u$E5o_TB{$`fgg+g@I%{dxYllEfDg));La||e3`G;&cWS$ z?=x|2SK%^oEyH~#kfzfjn~7^bf`s_C7-Q0%0tXv6ZUe({*q(50A-dbwjWAFnFB8{p zMqil=u6-NwMp^Pq{Vjrkij)UvciEXbi2qsO__y9qyEw3$Fb7n;#?T{H?WgigWTHU=0u+`tE4+^%u z2>Ik((N79xA3{uftI!{bJJ%`GeXCHmhaAUMSKEu)d&IoQjL&&obvpnnOn`Z*$4|q& zm2Eq?H@OGw>pHlSZ9DK!UEmha*@@Qk9h@`IR&$aT1*O&1zrxfeu+uFV4QiQ*z<3xAUc7v1E zd;({~cHrqZ-#5%GJ9;Y+-U%~v%Va!QR-}48KCkS0S0IIA4Q3v}AW>|-j-EDZ!15+K z=KIwkz|4Qs9PY%-f7FiK$z{XLsPF(Y|A}E==520(nVAz{=H)me%AKPE%+JThjbQ#x zpQ?NSH!y^I?vmJY3=*qAi(^39^Hq?!n4jP-Pyx>9#V*i2Q-Q;R2!S;AdPNR2rv451 z4?d<=uTs~T`bEc`=wCRieC+IsSjlOAuQGpK*__mAV^XtIV^TQqZ&uIq8(8O^@ijGo zg0GncgvpMtP1~V=Cw#qpbzOr6gb(;is(nStQu%=Cst)!XrXP5G{jWRM!ER}QGRkDm zb=V-7u0MR)wJ{vw%t+z!4NzvAP(v7&<|teY-&430!k9R)s`XF&jv8b9PK1N8)2|*( zrf!|?#OyXQA$GnWZY&3JgDFSmT!+=@#@~X!F($1!aIob~2Fzi(D38?0tA*Xffz?@v zf{&s_jH%}(;B9%8kVh;|dB|!YuLX8n-Ui6ahE0jtZFyBz{x}w3@^@8+ye~jr5^lzr zv^K)QmbcK#UlII_JU%1F;q#;UF63py;&;H$$Ywa6)sqd2-wi*AXkr+e7=S~z@@8W3 zk09K`I*PiRv`F=rS$kHYP`S)?Sc->`Z7knPA}t*yi&2_j4`RNX;|5p6BlA_`8easo7TazDp z#ORZb&UvowX0&aX0Hdq956;|n&bM_0m!r8BxLfD$L{N-BU%!i(I+D6>7v6oiFIIa3 zU}sHW@3wwU>N?H^H2qgT3|$wh<*!zD z-NCxwr)SooX^ty&nm}=$TTX#Fn0@&KWsI0tv{Kqzd;^ge|Q$M!CUnh{d(^@jVd&aPWXBI`S<8vQ`OE-gm zz;*>Lg(th2fMFU6f_BE#;kfbvhtqqGe@#jE9f+$Zb>QLjhkv*_>pTp*g%KOkl;C1} zHDCQU*lnZwFgP56%f`rUb|BbIxG|QULE3PAf)+6*+;QNIonE8C@J%ts_)URBFXITx z_5WKydm67EftPQm70<|<1vgtB$Cp_y%FD;adLh3Amx%)_4xP%5kr%_5D`=el}H=*e~KRCY!jBK|k%1cMo`!Zh5KERAu zuf~kLVE!)|_z}(bxF>R<{_h}09nJ8dDWUsoeOXT!cq`MxW7reyTNh5r=y~4n9-OQB zwtn2{*w@U*z*sc03B1cW7W3LQmaSD-b}!bQ?Z5dInYi^aRwypKO6r{(rRBJWQ2TQNDTwW{6A<%+=*M?){Zxk%lFw4rcbHxk)FY=+yl#H(r#sdTNSIOR2-+g z=W-Us3Cdap6#6JaYy9~fY#;p13-Hs~&2)(;F)L)kCcSk5UaPeE5#)_wr5uJ!Ke8ua zGZ<6<(`H?kW}Yd9bQ-qp0%FQ<^82(O>OMnARoUTW|pX2EWMz3sgLy2FK5H8G0Rg^Lov#eJ;j+wf z#$Z!^MKE=Nzz90Q*N(p=m+v=$q+`=4-W-EX`3Z5=?|)hE&JH{ubTO$j5N$Niv=a%zCN{v4k^!XN$ENqpUh?FrG^Atnutm zVf)~Zr>dsk^<0H*7*Ab4YeH)CW#zHqv(?PsOk8XCpRCZ?oyJiA3fC)e0gN&^*s{+{ zFb#wLodB+h+y&4*5D0f);jGm4o)5_$gt&VSakGx0+AWOOi>3q@zp$!P0i_<&Hski{Cg1lVDP*LKdPJXXp|Tn6JxkE}tie49QjICm?`v4>-*4cHbl_jPR( zD{`LWJCGd_nqY+@qp;5hU@7JbI2b=IblP0*@5+5&xyNhf-mmab^9TMy4C}PDb6j;F z_I}+59BT$u&FNLop)@vdovBI(90RJLqyer3i2$xkK>%D|AeV2*SHSfETsaupHE?~B z0v7T%+bsd1VNL|N&Q$($6>!yd1zhz+fW_EN;aP<3r8N9Mh3&(DNW0x1_Ibx0Gj8no zD_71g_t%%{sj{8)`oyTlu&&UsUeEsWo88ouA?4}1fW3+r`Ms+ER$BS%4vYR1@^ZnVaTG8YEXw^7vXwUzi`F1qE?9Ia zD;0(835zbpLpX>qI)uzbz<&k4^(8sqFDE(=cZyWudTAD zg6!#qf29I7zM8sbSETr|oo1aF*NA@c_Z7I)Fed^>Po^6eePJB+ZO$jby8Z*wsZfvf;K&oI2miUw1h5?-}_s z<;}#Yi>>^1hf@ckLF9r{k3a!)!Ku{yu{^0vk87?b9+UHVtCd-CD)#xpJX4OFqF=!t2q(xM2zvf$ z<{k)+)5BcUqImo`bO8Pc$B$`iWPsxrO^F}7596fR`b#8u=ofmWZ~fJ2=K>Ao(15bR z)?dKcV{h-~d9@@l^hj`udc>pvqk6fOcN4fszNe=Kco&0<<$L_PM55v++k8!jTl8Vv zfLCeve!xKa-s2!{EboUT3cXgkD`2N#<0g7_+>a{nxv@67e3bs~8NE1NRk>GTF2;*+ zi#OxuaFq8$fKYRouNoVk^I2OVKIK>P8eD{!vJf-tq*WU|WW}e9{2t-cmQ^bn>Xs+N z`J*bGv`M6C%$k}DnVT$QqGns!P*hsPeTJ<1DIHYv?}eH_tDfI(b$|xSl;BQ$^Il@s zTXa(DZm?7)W{trk8^+AUtZl#`4?$8-nDtkX_v0+-&Vn1Uq%jLEOqdTx)(hd#5?m&) zusRQ9U_w)WnV6O9*K@(FKZU$(816izW#_NS%3mCQCVv}YxAXVE8Tm7QnV9uLD}UW# z*1l*ENh3tkBAYpvbv!bZ3ugTo!euLOCT87?aJgXC%{gM$%?B|n-t8_htL|V$&(BVN z&&O7WBTjc~re=u$Y@zdn^1Wfa8lfwNwg_drrT>*eZxqV*NB#aCLcbyITZQL8Opf~o zG}Shrw+9$D_k9D#WWlg%UaPF1!h2WXZ0CH|CG9RSEylCc_Od0J*ml;XorcNtOa-9QXKBTe3f!VrGwMtQ zeUL&`J_cA%QbFco7CMIcNiV+5C4S^UgZ0j5VcICHPAeP+&?3gP1H&6?yB~Es1D%I z0|yNHHvNKW0Kd_W&w&7g)_?$ma&oHsPu^y_w9{1L(in8cqqNS=14f%cT&Yj>Iz_ow z1+eo8_-NR<4Ym(H>{RWtYuNdmEVjoAV5=i*Nxo zG8H;y9XG91KKd47%@7P8@QtMQ6+=p$_5@WsW6YqYPM$VxN^%qydW}v_D61%|NRGnB zsdY6ib)%D`rZq0Fo7cFud0Aca`Xsd9$?>Vw(dSPXJ8rad0z1T-x|-Tmj;pW1?Un~)9E2TnaG5x;I^PEKQ(D9rKYsgc zc^H)luv~Xo^Lli`Nh1u@)Zd~Ed0dyz?=$nqbWHvlVK;ez)wv(?vSF|D;AiJ=pp`!& z4kK?P?6$lYGV*8qGRH=fR{pxfn!ksnJY17Hg(H-4RRST!Xe-I-L7&>4XI;l^9etH+jVT&jq`Fv&f?kV|B+l~?2uHE_0>5sm_xv+)F+d8i1 zT-wy-#L}3ju(<>4w%&}iZAM-WGtMfu-rnIgcy@N>%==$+5(T{k&SBY~DV}_B7C0JAkJ0#&80kqgRfyhS`GR=Nbq>0~Uy#sAR(n5|YW`LCleo_t% zvIS;fP#!Rch4BD4wRD{4+13BY;Pn5GA?g1gL%~`zJch;H1?Uly;)<*tUpSSJcM)=k zF9eJXjZ43OB3#`6z|H)Y48=zvnqR_zpFo&MG%^k(7L7gz53klejXskgDw2rNadyX@yKOgSO@x}<9+FSBwZk~sQNs| zKT6Aw#P5ed!^PqeL@-=o{60i5TyatLg^oXFhLX|W=@Z$Gv|G+)mcq z!1#P}$7pVF{1Hl-qPe6~JQ@+tbCE~apB7Id7yfiQF)v^oe+D_fbb}k`NuB=5ECyIF z{$GgX&(iEB&xsv_aL>V0m2sF;ya7BHlwa@q3v|t%Nhb=vff+hWXCV^bO+ja0sr+Jb z=EYyc3i3-gyQo0QjeQ0;?wBOP4{`fM=+yfMxGjOSm|qt^lX2?l!ZiMm+=>!p$SaLN z3>%dHYbM+{0oL9FP?~jM7yEY=RW~{QmnC9Rb+hAdk$6Sb>m2_pnu*1~K#_MdqE}iN z=U|zCkLHSss%z^V|K2aC#EawqkBQx<;|+|jXS@ekH(u%BI5!scADphFB;!9p4F6k= ziW?FC8U4QfDaDP7pGv=n*?zs!F-6sDYaRblok9v6^88($=gHuZ>Bn?>Q{z=k@9}$7 zyvq1jLH#Gbqqu2B)vF=p`&!EM_}`h{GdkYv_>+wHtmdlX=QH)^N1<2nO6SKPCHLa@ zR2ddJky(i7Mf*;IRF@I@v2^{&_9Kh>{wVB`KO>kM>k}CQrY}#k)BRJ$cV>b| zs`<0M2UXnJiMfn>x&*FDJi@?L9-j#wVB#VAHhaw~@WKS&OnSEO z+5FPyyu{~Ni1psn2=14@8ckeAX_t6YP(Oa@_GqG>{H5McmH*pOJOb8sw2oQw-IpO| zz*>ae1jj-@-%Y5lHRjTd)xD1C^iq!yuV-R9qZPQi_G1vnboxZfa3AmpZX9Og5X~Mp?}2x-!^}xm*CuE|4fgc zCQ9QLW&T-GW0Ayd6u!`7Xsh-7mecAeJ9v1_I}NCw8RGAL<-B7yLW^`CeidGu-HrXSJ#iNRwBq*bAK) zE$q0I_#sY=O_Gm5_mzt=8`TsWku6#|M?l=(wXrN~3xgpYSa@z639-R=8#hFdTM(fg_BXAQw%SN{@Z zz^xj_d<-RAbB|Ci=M2GgM#mj>0-Y8O{Uha#)?ryWzksYhkze87|9_B`K9PUm9%BkY zt@eT|{SH690C86{3-eWxS={-Qcdjnx>2@(sSH(P27xQ$TPgTs*RWau|$r};XALg^B zT-W&qIM1o!-TepPH9$J-4lZ|Z2P2oSURCRK5qVWZOKZ*Q)#bIT8p_Mc$`P`B#j<7P zHO*_rHsEmBu^d4xR|Ajbs~VQAUc0=mD@k~$bU0p+K*-8$-RJX2gs%z2q)s4%}(>67y6;?bwG;OqI)P0fb7nhaK z_Scm8lgj*4%KZM9`=w?6va-v@rcPb`=dtCf6&qLjz3bKc}P1D-ux@z`=)%B}uQ2EuXTB?^_ zSc76N2h&{FvUYVV{>iGBUC^phYpk#5yeJfaO$3FPOq-e9EE)P+ajjk7S_d(@;Pnm4 zx}pINt;_40k>-i3R;YZes9Ce72EkS|z~)pptU-s-h`L6pob+l$^{=c&c2+mC)W{V( z_s+_V>Snf_UijG^5Ln^x30Vqt9`w z`GTX^s165ZXM?RqHLPo1#g@z3Utd>)5;fFa*oyk6!d{x6L#U#7it5@NS$LiUqPdl}G?u=;*=FU6i%o)}5S^G81!{Vzs^3_&ZRL!ZBm)ERk3aT8{ zC!I2ThI&3GkJT;4DSR;1=xXt-8wQ1#5QSfXcfD+RUG3Txe56jO{2MUY9EMCoWe`z?YcK{cDv{1$JXF?)hxWzV>v7&18a=0Jc%Y+&vJWMH ztWfq%}0vyEEJfp%a9jDD(`WONBNHy+r7>LO(C`9-$8leMacZ zLf;VjH=!{!V5UD%Xt~hih0YXume6{kR|@@<&@TwRPw4YPUlaPa(0>Rm;Lsw{Jxu5* zp_7E3By@q$#KorM^;Nf)I31BB{URMmf9cQ(&JI-2AG$a0Rvu8oKuM9Dz-OKYczGfeW9x9>a}67!wCprwteX<%}_Y$HBqa={FG!-xgzx-xN6Dvly6H!SElA zxIc=EgKE+cOv>?RJZ&Iyp0q(zMJyA-@F?*Ygo z4xl{#%Ncn$!p=A;Tr@S1$Nwf{XytXAP~(0+1WY!J<#!kC5Yc43zJhQ=4P%AlKQO`t zhU(-811>Bh)7E{e_M{Gz8DbxdUw%~%mWySz~Gm)48WV#r0_5C?!0!VvTcXElxwltTs(zAO@e7T50rC1 zoht4XAa-ZLjsZB{!%5w&;xY_|8L+RwXTUFM&)W()m^+I0U*IG^(}5~_Gm<23p47`~btvy@omA-veEPd)_ zFEeknK+gwteuWCuFIogOV4j}B1v z4;eXF`bFUFGFA6vmv$N^kELI=t5yIL|D4SH{QpOynKOx%XDWAvl}}OD#WBoZn5C>| zV6)e9gzpu34fg_Co!{d65Flo}EL|h!?>p{<8FP>Fhm`p%XZeYr`3U(tBU&T>-tfg`wNw!ybX16m%9e+4S3{bn>Q${!xdzpBz}Jqw?|QENqS=C4Lg5{L6KU9=+fB@T z;fb^DueI4Pl;0xuS`0_1GE#Vh6t;u6jq2gxa1<_v?s6X9U& z^y55YzEQ>)KYp|Aa;(PJVhh|@4%UGw$6VM=99W$>=%G1oY>e?E9=GN7kLz+#9?NIs z)xvJ#!0Jpz!N<@d#?JhO-+H&XzaE%3q-+@2U)W zS3q78ZpN6jHp0P{S7GH3LwP~|h-Dau&yVK6AdfQ>jiHs-9YT%!{iuj+IQwn{fQTj& zj$_F~4FhUKXX0#*F+ZFo9K-96uJ%l!Q2Y-_fCbJ_P4YoU|DiDm6rP&ueuEvAWn5R(==q#aU2wf<&T4=q{ zH9}j3ZV>uOQZ$ZF3H_3|?*;z0m!$QKzxQ=-S>Rr-y8`Cseg-bj;@a)>Yj;!6?IQ2u zj9J&&pw2(wTCdI+*j{AicZGuCE<4TU87Q0I)heV|`YNPe>BzVseHBu?3qYJg{nM1; za-YJD$^Z=fj=|l(4mW_}zIfpPis=;PXdDKY`6&W96a-_;I-I`>9ANM_=*OA7h!xF? zTmb5S6*qW&j&Aw>I*?d?zJjz%>2sW00z$+5xaY<&eRoO|t&4kcM{IYBhQBe)(N*hf z+_?pOZ`>Kj^IqlTDL@>b08TAg_M8V``=HKzN^PgJchf47BR+;c-7eH^)4>HImQ^jDR8iHSsR##;MNZpAW(qI=D=>^ z!0P-8%*(WhF@F4p+VU2n%O#$ryza)2xy~qwFvghrTLcGN-crabh8yM4o#7~-c-7W?Y@jDL=w!B>Tmi=o+{*1g!Gvr-p<&Rj$Yv+XJG;fcV+B440&p@i_xq2_gk6A}F>;ugBvFUFyZ7TRa>?vj)4td0;V}%m? zl0R7}-%Iifgf14kOz6ji5|%LBB|@(ddV|oC%rV`9lpagw9tuERczYM@h;D5g2RvND{R+?rzQVADNytpSxJzN- zV2=cjGtX{V0Ks_#2=FWx4=CMWd8;F4om-eNa`&NobRF|)7_39O}}3?E)2TD(N(KA z*TO}99)1Bw{5%|m&NTh!!Q=l#G@r=b@54Y*e;Zt4(MU7;FzjbA20nRLZ`>0+D(n11&!i{}sKC z(p)UA);SAT7(d7IlZ_0x0|6E_V>RGkkydOqSiCB_L{O~$75sX{;zkD7B7gpPQ;yr= z<4-WDa=^U-O!uSa+%0$f-$CuJ?lIHryzfUQXH8(JR7(*VTxxso9hAEm$ zI>nVpf}0tsjSOCAyydGsvlp3?|TWGK--$GT6ctXK9uj8C1c$-INg<8GIJ} z36Q5YGEg;pCY>ml6XN}|bQU7^|K78<%Pl!yIm8 zK&RfX>K27jZJ7m0Yi(uIz%UbJZ6 zIf(BvLO+(SAK89nQQz|^dJIayjSMQl^!<2xV6rCgWw7^Wj6RVX-21WyT&{=Ka+OWw zUPFJ@NtiBT-3x~p%gD#98VOqDPMZg8W?Alpo7%{LhdE#)16^7taV^}jRl$u2!j<(` z(ATiB#A$pIhAm9YW8mW?ZgJxOAQBrH+yltSjSSSz1t*AaDseLdpXjl;+{l2S7#kVr zbSEb`M;IF!{6NK>nmC4mPw{jYRGGM#foDnJ>4|mpJym>XCb(V%8yP&W;?7R6-^NA; zT5?sQ4<%Q5d?wt;fQy;2I{XtV@WKS&Ol)MpH&Sh6FqD=HJicsdBLmg-uJ?Y0;A$g- zU(@3f?+nzB&y5U5GG~{1Zz+GZkpXKvaumh-?*Ab4fNK!;83a=s8K@2*=F$ysXJQ1y z^-_R?1wb_?)DJjSRR&fZE8Ql2vr3$8Qp)z0K%nNsUDkzozhoYC{2TWH61sXM60* zxsky*#$6<}Ju`7RGj@*JZNMw7N^ED~#ojF{Ckqq&L%>D`{B&_615O0TMh2Q~N}S8M z)xutwIFf;vdL^oAv5^5s;INUwIK^Wl1F()?t5Q}Q8EmGsWh%Pb$Y4D4yV?Nf25L8x6~aG>jSRSB0bfQ{ZHJ*NLA7Bc17;v_#zqDw*gQ5eSOs2f zWT3tYjUIVc>sOhdCU3oBv5^5S?6{P8Y-GSD$wwgkK)y$6BLlvx*vQ~bRN`SDVb`D%d&09%1MrK5Op=XDIe2=)>%~bBCRR0tmA>2`6XR zKuT1l!JsIL@*rgQ=cuUOh`_lKF+4{tzBsrmK(>7a!ZB0)m35l1uvgmx6u6wr&`?%( zJC0lMFPL@v>=hd~tr}ZCuH3(<9%$ReGwC$8JA#i>4BJ-Lb^kMqQXJ2~GlzK`k2((* zmmbM)rE(vPyAFX@MGPex!`Es9F8(qHWv4CnYOHD)~kXu*R<;7eRc*EK?)vb-l49*nZFT>QTh3ZA< zT<5G7jfbo1!!_q>Nq(?&JAFLgY{yWYwlY3*ExbxZFN{{vIY2L~{&nF}SW>N4VC%*B zW_fqDEEC`Jx#4EB2(wPxtn6knxNCySk=hC&&>ARhf?iI~#p76(u2%Nf<}|6??3$3i zr$UyQGr=ASX1fKwGlSl$KxZLip96cvI@hGDh3$GxyjpnABI#`fWEpy}1|Rz(=*O!T zo~P}(5G?3l7OaeyJsmo$QtZY4;id*=R|Z-CuUF!yZ`Faf3Jn=A46==6%zA#aWrxbR z-jzVF-8cFY*)1U0U%_mX5Nr?-?n{uqCx*3mh~7biZ#%X~2$tNNHT{{}8K{n#U7yOY z$*yd6p?A=btqJrt2?t)I^S)1MdQX<_EByh#U&K{39ImmyF6NsxmiR*5Z@NFRC#n9Cb#P zvojOr;Levs+^hpr4vsaMIIudC3NZ)SaxhkoP22J|V4{(!7g8gy7IqT{R%aXj5hv3k zhT&+I;IidyhP-09F@G4?3*@!HZp(`mVT~c&j4^)H8QJn~vGRu^1@bcII2{RjNh1u@ z$lD0JEw9bWAEE^E%p4~pVpbRO?iAU4&NLrGxR*?2fM>eA{#z)BGRH$XX4jv|-$U?4 zn`)9^&N_r6#xll)|_UQL%gnQZq0yW`C(Ub!7w?iFuUg7|_@VPLnq^uKL-axb~ z>N|}w@(gdN?cTG_$WDRjSrr9=xy*&)GYXBJI^(m2g=T-ug$kunem}L}0p0G$MI92F zPheP&ffh-DBWRUuI1D>KXWZ{S_L=SSgPV>(=C%XfrytWfrM+nf3gz_^I$Y>Dp$mjA5z1W~8U7-nn}t3sl;a2V z-zKy}C^yrjdnuYODTh=@Iod|badpyFLe~l9980?2B=mzA-7T~zs^dYQqT=&RN6I-?=q#aU3tcI6ozN?Vep=|ALcbyOaiK2?g_2Ck z{~w{dh4w*1R{0mI*4ZN;CkcP1&}yL#LjObPZ9?x7`c0uv3f(32PeT2ConBv|B|=j| zj~6;e=($2y3;npztA*Yw^lqUK3w>7TD?;BC`gfs+<0Hv(pDJ{b(3^zbE>x|rN4P?C zvSe=ei+<;sjd69BQ`%ngT}^4s zPuEIj&TvduQg&J)ulh<|6?i&O+bFyKm@5af*k=Vipmk&)joFJoAeV2*7r|ks;-9tS zOb{&ac}F|GMlRpyw+AcaRagJgL@-h6PoU(aJ?}-_+@JDR=9YlaFeg|cpO0Yfkt)Cf zPGvl@f$sBrt#jOR26d}?aT2dEXJTPIr#f3=HA^7JR2|IZ}6J@R!Y6$&kGXH(M z3YS7y6Nm8_?iO0a7{3$28#}#5f#KU_jPc9-_Z^6G+>b!~2J*Yd^0Hi+|GvbE?3;}- z^5(z+pU%KMZPg3q7Y--9rDJ|BF5Bn=D+VY$SXz|<`33DUJLBDyh2p?Ft{0G z{4)Q2w_Ew^?%($)$V(bwphn(C*zNMZVdaniRi?b=-yr%wYNFf?J47@Yug@Y}F8{ROM7W1dBugIU;t>R9J04ap>wDfo;gdnEFn^YlvYLF8 zIS~J}e3qfHQ)j)g<%DK`{L}UkaqKJj4{Lw#*j|HATCl=LeRkliDck=3SlcrRa)@d9 zUX#ucI$P*Gp;Ud+zeeavp?p8+ev!~m3T68x7QR*J9pe5y;d95#t&Oz?Fej5euJM^P zVSLVO>=zOuqj^;*qSx3j#Txtmm>U^edhJfGp?8YgfQc~=5@$7WUH#P5?H$o1+?|^` z@^RKwUNA3mZ9BsEbF12XoDbFRR$T$<3)+d7^PShC{V~sRso8f5>2+QkWZ`Ffien@M z(+AID!jVtM7wUtRc=XdDZeO<&@8+JJ#Z=}R8K?B)AxufWdnGd4F+7@SDS9g1!*P7Y zaoFN%j>hGljK0+TNpO->UVaIjBV`DQqnUmQJC0`h90SSGOfS(VJerB~XXI!mEE#jU zI+{txiad|d*3nE~gxv6Grn|`vpN}LtYti?~>Z6&~qwfxmX6gebs*h&k94*XfJRanJ zTmz${<2M>BD*RW$8rrf6_9 z)6baRGdkYvcoWlmR&!NxPDqXhM>Cy6?#1sxGG{bKc7V;{Xr>n-|ATcj)6K{cI;ZCo z&$8&@(M(HNOXl#UNTSMSV`|1-;9HpB5NtF&n(0W^cX%|@2*wSMW*W=D;n7TOtjO?a zrjOD$Jep}HeZ!-f&f*gZk7l}`357>9ac9H3>+TKw32LiG}D<393IWYH#2G- z&BTGAsC6_G4-1Q0M>Bnd(yXJIxXM1-`DiB28H`#-GjZ;6VH8I*sjf9Vnkm6>;n7UL zB-_o=Ov918q9~4LdV|#!9?ew4c;V4Z17pZaC0pi!F&)$AGGA%5F1*9a=HZ!?N$xJxDqBs@}uM3kUz|wi0otH zBK?wpS(6Yn8XQn`5jf8|m-m0gsfc?y)F_7|spF0C!^a<>PNS)6nvFh&C^(!5HEkv* z!pd1w>4y^?R^?w*R&nJc-$|8c-G|iv2jwH5UQ{@}D5-~C>4!!Evm6cNFERe0B4f%W z+kmS<%M4&22bBFFp#6ISQ05e%%qdJ=45;qkgL@9-nLW*Qt(Z-z4#`nRnV5N!ou)aa zPao5>q5d5X>(OmBmm_<^8IzqSc)ri4^z5zPLbf%~-N^fU)X$!r^0TIHjXHlSZR+H~ zBYtXYo9ixc%BVik2k@+FsBg6S)vK_FvgKdjT(c(4p=oVx1DtSzPbj43y_gxu@pOFJ zm0p0~0gkiT-vxZ6l*!bMOvT5B-y4n*GCsrMgVSkV;OF6H8-}IH)Qxl%E`_ir4&yN# z$2W{I))V1i?DRVhA3}ZwjWK?ix{>)T2Qtrcbf+8nVjSzujSz%2)=b^VYJ6x}50po0 z>V;on69-l&iMb79Xc1%TIa4>%3VFo{LwOiQ3*=?$MrK1Ef(FK5w@Px)J-CFjsuG z%T-Qzvvc#_>PpymP*=iwG=}wPw@vaNF`l600}MSvoy()afcd@_HE2=X<;-a|K5;JlZGakRB_PeF}NLm z5>B1P174lQ1FFvA0aa)5fU2{2K-F11pauC_zB-EsygG{qRGq~Gs?Op8RcG;ls)mc2C>MR~mbruh(I*SKXoy7yH z&f)cs3PXLPZ_n4G9#Xa? zjGAo;^Fmdct&ZaiK5HWp&fqJW5kjpwmW zXSgNgIO8c2_4ru^VJ*g@63$x+&W(BR&@W#}_M*|qd0@S_wAahD=X(txvHU#u2)a&o z*(cL51N!Wmqw8p`iZPq+(W7`j183ZW?&saL_wzB@ra5&QCcq?JUzwpW}YDJ;jB0{L}e|!<0A|v#fiiNF*k0X z|C`rv{4Mv1b!G+JfnQ(tFL2@v{{`{dtNxPBiSMZ0ymZ%wfn>Nd96ry=%ob`0{b=~A z$8hx&ZVbYhIIudD$NwN>j358;jGca5tHHO#7~?ku4)`<#rWg#d8z7{EPk8WeMEqys zz^d@qHd@3OzgggIc@KeMxhM~xx9}P>}_p%+rXIo#o=eHlZNOd>IX5vSC0?l)GSuSCjGLeondk3;sWZd(cF(&dAtEru9}HuR$)Dj3wcUIE7qoN2^z0PLo7SPFcU}wj{+&0&K0x*h zqYdhsX>W7VZ_PDOrESDVh$MbRo$ru7;zx}s<}?&RsZ%5%wgN% zRkrQ$mzq8Qs@l&8^*BX5av>z&FHk< zHpom`_>Jq={tw^p4X1~fm6I=r|KCk^`*xkv~cMglqYrmE56XBjWul-)w zkDst}H9@-c9m6xsPj(9&bzmV9d0 z#;I^SGP3hapu<06h5M+4n-GR$nB$X?okkYrVBTN5lYFYN-HE6x4|RoeET{CDg0fF+ z+p+PZ$n#i~4|I5GSU%?c_!P<_b-=O#?*soo)o}x4{ms3-!?DVM{wFL0^0*uIfU=?t z^V%;1-3{92%geN;+XlJ!9un3~Gu8|Rb+Z+6i;*X1 zgV&^FFK)-Tub>2Va4zJdj;}6BouKUBgWXB#JtI+1gWS}{orh!orIWg{gJmiO&$N$7 zUarc>vbkHCmkm3e;-@+mA}_=6j7#x+ebh}U>j`CI8(}=w5A(eZ;quXD*k1hO5C(0f z6m)OeN<`}67|8T1AZIF`4`m*K=R=vJ!{>ASAl?2-2I%&8*6~ z@$T|J(4ME7cW|H0F6X1B{{RogsrFd)!*hfP{c4?$a#DrXRbQi z0UPi^fziLx@A`^ye&^ze9uLO59NzeqRx@YPex;*%2oq6TYkJ?MHR@dog0*mmBVM%D zErF;!!mx<1ci{As^TFjWheW@G9!~62xN)X;FIs$)nl}wWFkkg?2JuVv-i1RdztL`u5Crp8Pet~ghIODfI{r@t^^T+q zL~;jC?E3l{%Vf)9o!4F5Q z5#G3XiiwUl<(S5Z6HKbd(62&svG@^8=V;9p#?{`B6E#;H??t~!n(H55ODU5zH!vP$ zZjaI2;P^M_H$`(vr}$_j!F~8*t~V`yE&Zm`iFL}&5WN}X)DE1l((_~%L+!x%pGJpN8G5rwh|~lxeJxH1HhQprUFVx$z`edk=^I z6Sx+8YQC!XWr@fgIK3?rk2`RBU(rk~em_Ot&4}EAa~rdLkLL6aoZh{6tHkvVoZfvp z-oW@)x<0_V;SQW1Zzd5R2`Q6qz* zMM6fBn#m*-u~t#=X{%MMwDt4Z3T?G&?bEONX=|xfv`%$wooa0z0Bfzb_O-9It*_tr z|F6CFIXjnx1O%s#{mZ%kz4oy7aP~Q8<*a?yGVM)H-N_o86iqx~L_gFdZC@|od`VOh59+NRF_JY$H$s0Tk_2q@v z5RPsL$sw$d`JP4A&tR<4bowt^+FkLB3;5b#xYhz^FkN#erEg>5PX!yL z?&S1nmYNlqThNsB3UZ#Ug{P;RnL9^wXQn^Rb22yBBz5Pe-(lesv~WfG2kdi2z;i+i zoZn>O_TU^Tyg1D(GguMuiWCc++*BBRHsHl37C5gZvsJ;`C@^SvmJ`Dh)|5D4m%HJe+)JS0rsoPtu^U04OeG+IGgJ)r;rvn=aL-Yo7txY zPPQ{BaTI=sJ%k_rA3QL?e$~Hmx{7_KksBpXVr+D%qBnOQljtV}UbR77UZAg$2$l zS*t2ISZKAt8B|NlVu4c(SJX&#vB1f##X+sou)ujA>(?m_3!J=Gf_kN4f%7%?X1UVP zyd^mZ_DQB~UtH`MD)y_g15q+8a31c`u)w(#^xFaUtIF5%tpRCHD?jH5+JY7#VS$qo z-nfK4EO2s37NL(y59f9CHkRtXyjF3VUcw~qJA|D)BVe0e@>cvtJ%I&IUOoO0Ufx(V z%p8>96|^5$FVc~WlouqOo_Uj0$8c@wy2v{f1^Sin?CgIJWFP-zG>ngcq#_b2{}>CL zd$A3_f)+UUl6&8x116#&f3Ff0^A{d)B8KJfB|J_#@H1e=aL@(#@#}bJf@MvLIDWkl z3%tw~C^HEh*5NSaDkN)(DR-g}CwfI$)EZfXSKaw)D{OljFj5=gw}F zS}azILRw&Bw)DhrqU?my@$)t|lHr^=*=$%iE?j-?>@~B>!Y93P;gs2NgVGEb&T`VE zCz3H?TzJUr&j^?CrNKb1o@u3v7JjO95^%xnSqnVij78ICR{(|F#^&g7&VUA9O|CCh z?z4~?^0r`U(m1YTObK~$zRad@vHy{f;P=ZTaNGdCw5mS^$4ne=f+=nMaIe4=(}2|F zrFIPtmL;Es$a~bu|3Ivg#^y+C8mijId;Fc?RoX4L-J2$@s;$LbwKvj!;saPH4iCF+ z_8(_01LQHVEe7ZWq&yy35_&2}QPI|6L6+mbIvZ?sDiUm*d9Ohk!zZu}Hjlb#C)rzz zaWsG@$3cnPj$=OJwhp4+BTz*CoAX??dDKn2{N5aC#zlUd56f>U;;b_P2SakO=Vc^j zeprtAXDFIf&nrQ>v6cX`978h>x81*>UB0JgyLcTPwR=9|wy%hK3v=4FdFLR{<(DXq z$FUdk>^Q!PxUGYz*VzNVE0E{%D*!*+KdaN_{9K2_<+l#}xUOuldAA{fajpl3EhzUh zI~kyv$K!5AfP`CMM|6@UfR^8bh-2-w*{t899F{^9Y`MLWV%I1$^C0{^7nO?nvAnZ9 zylWunEkh#M%FZ%&mE-4}xcuIQ!WFu}DA;zR1dX`qz3Z^u7TBIMQQ)_&&YCMf4xy}E zmmVN8aGc_cUY?)Zn&9@1{J8?-5JsMwOb}C-3_%+4r6>nF(Pw{cZUn#z(_$S4`EAsypwl`8St5~ntqIkCA z7ZtBkyk7Bk#Z8LxJ{o*~qx7E?|Dl+~+_2q!6c1LMt++su-+;4Rt0MQ(kiJFnPQ{-p z%8M}M%gZ$2aGodN!HQEAD-@S1HYuK^c#+~gijOP4p(y7ceEZ?jB>$m`#fr2a!Soju zw<`WcF~pi-{$Rx-#jN5nipMLSrr4zTImIt3{x8KF6z^1gK=B#HEsB3pOke>^{}o3m zeo}Fc;zGq5#dbw;Q-pE+htfAG{!sCkioa8QM=_u8F;Q=r;%LQk#c7K36{{566wg(> zOz~R9TNHny_=Mt1ihoor#5K(JrzoDFD0`)mzE0^sDf+nav%J_<#aiQDH(uwZntqhx z9O6Jc1C>5av6YDO9g1ga`UQ$t5>f9urN6KEoMJPt8`Q_G$qR6Mmir25OlJ>vSUVwY zcGzt-z1x&3pH!Za(EHx&PIEht_SMkmW)}NuxThuoyHaxtvG)RdYq+0;_N2TF_m0fp zOu8I-*q^-_mk~Z=VEHe-npK-oQWkr-1LP0m9`5Yg^swx-!LZLN`h1)UuK>2GynC+P z>J`GTrr|Gn-RkyrFSph0i-A_|@m8Jx9$Qm=w>8Dh?Xlgc1zeRl8;~opET{OH<(k@fvA^U zgP$+r=+6j5B2z1|hD|01+`%I7g!ye?Y&U8isgVouD|Vx9L8EFns*;>XmmsGg5mMX< zjom2P(N?=rcOgnon3I5^Yd0!9lmi0C6u+dgR5CRm_jXGaq>gs-L=PYK#ctFR)S@TM zHuNuy>_$z-q=k{)s8!4>H+@K^s@Tp%Llva`B zCT~RQ&&=y;H|i-gBc3qtWnN@A>R7fA+l`{tl`ys&buqd2VmHbM2l0d{GaK2BqABw5 zWYeKUYAG9y>_)LK;SSl2q5wrtm;pN!*^T--+gPq`KsCVy6^_+kbgz$T&-tg2vSTeF3^<`3#-6(Dq2_w5v z{n2cCi~3cCi~3{=~F5IrTMiiR?!Gku7ww8x=-&qcSWR*^N4jB_AF{;=ISMUM3^y>>lF4+IXTp*N=`k!E+l_h=Jm3ja1PSQj>Ebqw z?M8iug=4!>D2q!>+ntoYj)h~pQQRj4Pna}j7ZguPx3O?+H)yfWblb2SSUKNO_-r4c+~QavYn_)E#mu^aU;GaS27 z8Rl_(Nb zUyj|V)5*=T8jyitnb*3I*(m->_$D$`rES`bseUyuXdy0tup-(8;tEnQ6&yf znAI{h(8Dvro2@+Fp@(k=%~PQCa9&5^36s|M(^PzWMJF7J8KQ3H#{+#i1!v5!<9~g7}UT14l>&m)z;EJ|3 zAm8!yWWS&17q+A>^)Cq4hBF5ivMdAB`M=s29Ox#($ygbGOd7p1$ATSxTwXddDZ-aw zrm%EY(pJl_`GFI^+Q!<(mPV)~D~(G!=#$=~mr5|DVI}w5Mw#nMG-5p5VGHU(!5B4{NrU-5PSeQE#Y=v{yxPV{_Yz_PR>?#NBbvlph|Y@*Z!LvQeOm z?}D2t~DR2qDAibe?!p>6cN)FZfoWf=~|!SZ;LO6aK^+pu+5kmaaDu)#*BBEiO) zHxkb~J{fJWd6ci6WN!<`L3>ji2c)iO9P<&kbrAJd;hoMUPJ)ddk35&(yLi@eT;xaO z{1KPpV4Vp#7?Mz;7MiI1vK;es^}WMUZgNgJlFc~Wb`wwtWst^p`TEJWdp_c}uZVgJ z!LO7N4L0u_&@R7G;Ah8yDiOb2eeZe5%qTB{<#z?*F2Awh$MLd%cz0X&1PLPxNmygn^5jyTgl0m)z2Be<0 zR`E>W0ayT>ivWv(_l{omw&)Np1CORy#weC6@(V7e^FBu8eT8_MBF9L2t>VRsS1aD2 zc&FkfMZRrdJ$}hV+^YDNqS&}X`Y(gnCE$p^|IL9uZKT5Mba#l{tIndXa)E6`^vEjF$|^UXZ@h>a_t=<$L7&~&kJ z1)A^6Sx#(R0iRS_Y+QjB8&|-;X?njrQ(kObAzf@-0ma4@aH{5ujVsX0lolITpvA@& zP;6WQ#l{s-Y+M1w#uZR(Tmhfg`eNe>^gBu?v4Gj{;fiA83bfd`0?yQQv2g`jY+M1w z#uZR(Tmi+#6;Nzk0ma4@P;6WQ#l{s-Y+M1w#uZR(Tmi+#6;Nzk0ma4@P;6WQ#l{s- zY+M1?Q2z=P8&|+@DJ?dxK;Ngd*ti0{S!uCx1-cy9H0w`OJX=xpwn!HnSHS<)bl&Kx zH%%xWLPY)qr4Lt}rRhyv=O~X`h}gj5eTsBeX}SJE%l!jC7Qs@=;pW7560K!MncAz~ zby9h@&wAI(`7p*%*ayr!_9hv z@v{!T8RmWAcGTvmvDFb5An#4&{Xz3y zcJh4Wy^ini+5V$WdVutoQ0En`bGwrlA`jpD@Arb{UE|~>koOewexZ45oxD8cJ%YSP zHLuCZ>xaDikoQx~Tj=EFBX1+}?$*4?PTm0I{U7Ari9AfN$aP#_T#FeTB{;YqC*hca zVIAw*SOb{w8Ze}TYlv1yO%NF1`RhJJ!up>1TXOR z@5y~@H@=v*uOH_JoDAcE{+AH3`tc)iNvVGP9YmoY=Y}fi$4|j?%Ie4OL9OofKZUVGG!--bq2KhEcOclz^qRX@(piA6ttJL^UI@fmC(){l$AG}ez_My|c+$Me8J^y4zKk$!vv z8J}!An@D|)jYj%$_GQQP&H)IZmb{w65EaS z~#rkpCwGr#bUu37ZqaWXlLAd&H8vbzf<3resSU>&{ z$sYCN!&x`hk1yeT#`VbA*U^EfI;KiQ>&MrU+!_5iuT`rbKari>UHb7wn4w^B z5q^z+{16P-pBw4Nc_%db@mZoDUqt=*tXlNWU#R-=7cfTu-=zjiQ%W)}nAS=$=*Ks5 z79#!lt)K#rJHz(q$LmV#s;V2y4jWfKuDs0Z>iPDJ|9BbB=Lp7iou zSJ2DP9av~o^DN82bpAhe2o6LMT)jNzFjmWhkOCv{Yl>nGYp{Yn$=#xrHz$-j_+0h; z_LcJQQGaa~5kCd3tjAe{a=P|I^L6*msPo^OBHo+?uadh-yvpVkO*|dgM$p(%+u_)I z?wdZIFSO12+Hu3sJN~)%9?kq7%{;#8ULiiyj5D-78hQ09wQDr;FqMOwKV%Yl7t9Ai zIS%*1U>_H`I{7#Ef|{Vc9rAexp4XY0M`evB^xY1OIvi)KM0*X z-ZVSGmvcQTO7X?-*jk6?T6j6?w8^JDYJ%2xXq!Xa9Xfl?Jp=ndojs;K3d}3QVdZ7! zmqfYdyv6@@#439u;lvty3em3jDb5d-LF7dotC&^fd1pFx97JBf#3_o?6=y2WRjg24 zs7Rd%>n%}SrdX@kpxC6?rr4=?rsCO(s}(N+j=%!`w&K%@Z!7Y@dFGE&oT9iy@l3^a ziu~7>L%dMvfol;V!9MUwG7LNX_#Ha;L3mU2+jH!N!0d14UI5>GcRrw}@6PkG>tFdB^zPUx zf&B?D58-#`+x@OQ{**l7x)ICkNpFmc#dR8%f3Qb=vphpEg$N1Ew$bGid5`r!jUTHm z&LE!13wau0!~8)~`4+wj%1?%Gnne6U(-i*JBtA_lKfD!?%uoD_)C~V{K%Ug1{Nk7S zMF55h9R6wjR^lb%Sjk`L<+m{7=QslIHvaCK&yye?G`1}7=?kNs`##7lH+caXeexrd zne>JpoFZ|3Iz!xia+M-VwX!FtOU6jsOxm}2lg43{F@hw2) zjCyA*zwMG&4c)zGa95XIbGh#s*5eYIf{s*mA3zLtp)7{xfvlL_dP>T z<+3Pi>yQu2)iR>N=1m1{17Y+mkrh6>#HSfBiemR~jEwhp3R11{;YjA&pvhNU>%{w)SSJI}nQT7DgfyZqq% zrrZCgTXXm|IQ`>2%JRD~hu`t|2v4#E&{nw#ab&gGtU`3WFYga%0OBrN$>B%7=u#9G<78!!=)MQxymPE} zEweCf?36H~!S>J67Txssa~ak3;C$9rz}Vw%B;C z87gq_|9AYG83oqneZ>ah7C#Q~550Z9HFm5tiabP&W{zue1S*sh(QKdoheRd$9}+kQ z_2^qHtH|#!Nb|ZU&QP4IxIpn##VW-HMP5Iww^~vD$Hc<9j5P2bMPBcH@=XT+*)M1#@@V%keMgf>-=|E_oF`Y_ZW~$twx? zH+l!Yv2YjM+@oJxYx8JZ*;LFQb+vialc1XG-L|Wbp8nQ1?f?~Fe}y5K3)Bd(JfRG{ z`1}X(V+B~QGzhR*&n9|DV8qOnv3L zD-0rogd+U%Not?}wI(_GWgY|fdn9r6R&FfH*gA-c9Qy@EG}t^ot8AQEd@so>#Ri*) zn|2pjY&B$)8;~{x2Ok9-2m5tA4qFFNc^~`&BN}YpY|vm4g%aEpS)cs!aaev!5w~>^ z^=`n$oMA+Roj=}vU4B{cD?}RkG2QaxGuGwz5cnO4G#hN*=}2(-O>z21`N#5GgSf3j z9g61au5J6ZO6p9G^ut*e(#w{nX?%@T4J;GRYtu90%u}W3_7(QLh0B!x+(E`B~bco8G%D z>-L20xh;}rZ{o16-QoNkOTljItnZ#N(p@Fr-t7u=CgMQZ*2=Oxzm#R|w|Guad^JU#)nL;tt8A&22j%lOA!n`4p{pyPlJ~kx3IT zm%`pAr9EO6L4O|R#vTNk-Ts*0J18jSjegh8$dx;{k=)V zFJ;x&Od{n>$f~yhy2`5m!wi*GpJ3TMbN~jWiV$aDPbjO7_x$4|zaqb5f{lx`dYp); zODV0+jil9QQ0=27r(*f`ET_UG)A&Qj&#j$U7M?Y3e8sFD-qG@Uv4ImeePH5Jp3nVo znA6LDN+Nj`cEB0Bdp~Oz0Q0wzSKqtnCxiWHh`ki0A*0JploNK`v}s3WO7JRrOh&dD zXG-uMwXUk8ZcL_RT5E0Hg4Px7HFfQ)GRMxJmzj{wPCVnV((z-wV|h=l;_hU)J3}-N z*+(p#K5v%SFMzssT*s>B&Z=r)XS=}$8~d2btL_T3PgGv?T(9-xRyxLQ_ zTzQq{z91aTkL8yuuks$mE5!y|?kFUP5qGnv6WZbR_zvfmA(`ZqIam&|5IKKii{n4 z+6th30V|8{pf3MC$e{KGX@7k|`XtY;>8#u!D2Fh}-RXT3Iya$3BXrL48E0TmAavGx z{tU^6%!%kjOcjxHhRB)ou|J5fLX(Nd@!MbCit-ifN8zm~7GKZO=0l#J9h)r+`tfBb z{yppRF4UDVIZu!=XAVqW>c=wX(@2eE%r=>pZe>h+zEOO4$e1$3kDiRl347mV%#X)b zfNsAza95eRHR97utT~QW@+kR1^o#g)Aa47BsCO0kbtj+k$>8#{@+kT7K4trNA>uB- zJHRi4G#hN*8YH;d_ToHJbw(&f?s#?=mz9j-YmBn3!*!D^d{sXtBqwC zXn&)-@#e~-N21(ari%R9wutCb6l!s@3X#~`UVu&vp8uEHVEI|vqMP3P`%`-rnA*L+ zFXc*&1O2q|e~UP{t#jVEa^kQn_X6-<(cNAEBa+Gk#mc9+O;b!Yk2kJ8>ij>>@@jMI zXCn(?d$v1cX*NEQY#lQ`&B<$@@ffs?tcY8_9f{-98aXszlc0rNc$&rBd^YpY>`*zJj6mGug>XFUZvz3c(?QS zL%Vww*O@H7o&{E@@gsq?uktEo0CUfL8Zj%cvLTS&zZ7*viEUnrzQ2+t|3{X-Mk;@9 zL4D6JW_6zT>|SkKQQcD2T-V_`H?D&H`sGZjt)naCU9x+%`|o7kg=TJ#+sdq0;n|(U zLZz(2CnU>}{;0{U>oJZS&~J{TH<^`t?QO79L9Wb79k`trqMes&#BCk&=&Q`i{jhkn zOaCZ&Sbp8fti18tVB5--S(iEe+n&ryAD$V@3~2e~%BttUQ--a9iI!GV361 z%*w381bCn5>psp)Q4hEs%Wz?dF-?-P5Xq&BE`QOKUQ|7Y7MgM zz5por`HwVYSA}X&kX>&!iTI`L`W=(Fj#Pfw2I#8br!LLPuFtb9|Ecw7`iwKMCuTG1 zbjg;@sMGn@(8#XSjqFPG{w$5QVb`XVU7-6?y^qLiSFXpNrPxP3zqD-5@qO5X$~oH) zhdHA!A!g-Qv;b-+#8(QGto;6pv0bFszDu3&Sl!fs`K|KGQMOm+J9uEG^)Zjr`L z0&h9?txyFsoglX((7f?bUhA#gSd_7K5S4eR=QEg*hT36=NAFCPczh2va`!}bADZ!Iq73?mxs{PBf>+rI*p_sEaw zmY-;MbjemaEer;q-5NI{i&K{j+`Ns|dG$qn!S2PpAJX_;vRV_3Pkgd2<}} zXE})x4L0w3(8%HgN5UbPi0;Ol`wn#m%JoI3Kbp~Tg*{2~xmdY^(DRwBQM1=Vu?50L^vp6#*MNj|Vnj!mAR0ND-vT0WC5pg^{%mykE<|G|^GpHDf$+3x4dw5!llcnaF%-_W ze~-43%Rv4T5g8GlL%#>T1dVR%Wd3aA#76XY*2(-WG8FptvwK zk&S(?2)~?v<78g;ycds1aiHNHCkU5J>UEa9lkSv*;!!E%Wd3TQN>awj{H;Qb9SHN0 zUif3vLKak;7v639JPB00m+NHy4yiXKbrh%az6XSwHn6e@Tplo7rl&GUjg9F4lzn-` z^x8U^KMc>$pm<^G6>@p%Q5n%|6g2Z6#>r)PGI^)=C%i)m%@wF zyfR@#pI4;oWS$qBIGLC0dR6cX6c3AE%u7GajB|n+n8L95<-Bx;Jv%peN%G&!!ztiw z=ZOLL(C5>C*lLvh8;ZG3=9z}8Gd-WZ=`W`+z;s_ga)7VX4Q6a-P~u4Z4to$k6v|yE z^GqX~-?H8)-;n*%o7t(+{&SLB;3XO1A$N_yOQ@6if8qb4CmkpAd^C~Ue&lwt&R8P- z1j)q#O9#ct^wDH~O0W#6xKz_a(Iv--zB-v7DAQY!9>}^&f=i?)k(2p*rJ=~lydfhe z^M*V#O^r&pEJ(}LuJ+S>*#sl{qlLc6Pq&h{V?R#ep>Oun|HF(r zrEmAs=ke6nE4|TAtCM+C{(j#}auB>2W!m=j66$209f*?iz2rof-pAYLG|+F;$-Fot zXbnhnS}$gQ+Ja^w;bfi>-nfK4oXm4c@)RgNoY#@-Wd1jp#C_*+y3YvMrkA`0zfn(y zd;9U~@rUs8#-d^7paidx{kVF4dyo00q(6w?Bt3s~ZRxrQzl(}_&xnLVQ-FTI{hJ^G z`5Te!AMPIuV4xHP%lLE??T=?;{-P1}*t0plh#5;q(7LZbUTV%C0pkoH*pR>AfOfp- z@{b@Nzv4jub)r0g{e#%mcC?i#q8AH)sIc^rHiRlD|O9jiKe z;;>UzqRc-q@_sV%7LQ4=PUI0)$MJ|oR-~{DaITPo>z#! zWzzX}yz~FKm%G#<`IWL`&S=GKA`Bfb?osG?Pm13s5rEX=fw&u~1mR8im>($22 zELv%7f`L`^v5GCyz&yCtx3zZQgRUJnCbq}UygA7{V{p;l-c;9uO%RxDud-%EdwX3= zXJrkj<*n_=Z|tb7IkU=~*)DJS|Nr&?j~jt_ey{A%JHY#|d-xdd^|tRt_&|WXBq4XN z_tX2B_qaomi|4tNgH*81zL|@pcKpM#42R-id3o>wdn%Vjo~?tZl)K7Fu+gbGzd96rs`3(g>+dp(6 z;&&n9whoyb0)81rG}yc~pk00=oc;|&o*i%QUhm_;?{+&#lp{YL8x#Q&{s~3HBufBo zyEh?@wG!zm4jbGzJ;jMAcUL$3$kzx#Ci844AF@c~KeC)}c9qDn+BJ%(cQe+@!Hj6I z{j;=1H~szCH^a3N2V>{GB1&+3!|`-2y({*9bFLT`;-KDQU!lwo^D;TK7YTKv)D^o* zs76%vj0|GFhoJp8j)gX;d5!k^EhE+%_u~h^F^c7ipH$@ig8B0lPf=`8sgi=7tzpeO=BL9gbpCZLF#X7}i#m^~zL6I&wrG3TQ6z^5!fA!4& zwc^`~e^<=MVrTv^#nFnh6z3~0Q*2Q@Tk$^?`3(s9+^_gc#n%=0!NtP-(Tc|^o}hS| zBLBN*xmAi6D*i;i%M_1R zoTs=%@pQ$r6u+Q&mEw04?@;`y;?s(o75}UlVgXD46^j**Qk<>0K(Si!48<=iUafee z;tv#`SA1RZZ;Ji+x(DM;D~?h;OmV8>iHge9eVDosy zH+oOMlB_6vmHXccGwafL)a+O26>f&jn-I@-_#7Xy8R>b05YHA;?|9;73~j)a?AljD z??mATD?p;FB(H46+O4Cq_iWyny`TJGS7-fZrWbf?Ur%ONOFH{PejBq-zv5?xyo%E` zU?_E(nf1_V*1S3#y3b^0E!tYYxoygiU*|r&0R2Fjydb-N?%(2iM>S+Y$Mx1Dt~M&fPD+1f>EN zP2>eP0^lb63%KP%!&wBXUk7s%BlDM_a3V7kRQ`?R(!GChZ6ToKF>j4ne?K_Zb4A{@f^_Q6l2-+LVcSc!wGO>VQ!@egkJ zCsJoI>rg`_Q;n!#se)9-q5AG0oX=pYHrJyw;RHL5=h)6+w$&$?S8iJszt0m5Rgl_* z$qNrRRAK5|Qb!nSXzF@$nPjNpsRKzJX{Zq?d9!_#p)y|K7*zZXni19JerzxD4-NvV zO%@Q<=HJOC_76Usqwd8&_|H&RRGTuhk$><{lhetj3yD-M8AblV*_R#j4_*RpRBb-Y znvs8S`d$r}Ya4ih{6*v@Ze_!*MkX`2>4i5MBAMEo zEF=HmpX5^N!9Vy4cIA7f-tg3$q;7u{zX4R6qUenLgP+OSz0>HuMy1xXf02LiTiM=S z*P)%D*s3Ik;?h@mP{ z`?F_}fAHm`p1N1Yu-FTz*s=b>Sz!oAH-wIkhmgd4{~Npg0~G7(A3QEx>lEfSI+Qh_ zlfdub%kbmVCAHxzG384w=wQx?AF)EMuOK1$BmD3*_g{k_s5XD*()b6g1dq4#(IZi9 z{umMX2PYHiUrs>T$OCvHeXL6+jW#Lv4}KEs9<6l?)1Lqj$3OT8);(5pv*~3l9Qz0V z1t)U4=1xktF*o)PzL2@GfAG;fBeS*e^z=DwDE1Hj8y1fJgWt)*Cure{H2)WH{DW7s zaO@wPSElP9d?Hyp{=t`V5RQNFzcBxtfP$r{HbwP$Zt!%Hw5T>Y+j(UiPAFRGVJlFU zg0!eMQAxv_unS>$|LaECP1kj8`G^$&gp$=E;mNo2Jw7$8#%)#fMJ563^asK2YF z<*<0YpRQnAj(_kqWasz?=MxU9O*S4DL$!G{BaVOYd8D`FA3Ru!0z2X#{739?>>r%g z?x5PN#(08as5TkljY}?5s5aX{7oiEIhpz)Is!d+2)<5_vPRh>t2QL9{ET+f7c2Hs? zev@19!?mS1ir`KZ@cSh?(5YYoGxk3aVvm16+V@AwA|(sTGf-{T<4cE(XM4OK0*RC6^~cebvqu^JhxL&G<#wdIq%%DU#d_T_agHBik& z>amqI?VYX7)wasYnhv2mG$H;Jq^km_sk+|y%x#+pbs7{wZoBZ~?69PUucNLeN5R)c zCup4TR>OdzQ3y7`ej=x#-m7eFsiY#W(r5!~T3b(VWTB4bO)KgQOa3R|I#7g)u8NvN z)jmPH2%^2N1J0?jd$6+Rvz?+#Y^|^7W@Pl8BgVMx6m->UnNfl{WwQ`*32b5>sDaYDzu6FMFkt?@M z-wvP;M(f$q*jnA8)|I-RMfSR?$>>(QC|b>QVG!st#+0LWHQFnA2P)&NTvZ2`NIRiS zZK*r66X#ge#;urJvnr9$QQKJC*wRQPailJud&0CimGfszTQq;ctdnL`F62qAs*UH^ zsF$6w%IP8RRidplZsIE|P498qskSu!<86|o&BUz;OQ;3D-K|~|XSIsg7*1eaRSUNI zRW?_hsf!DGPj4mK6LvMNE4VN!KU>8$inU*dWf$qWt?t=$SGCpA6<=B1bh=q`GAWH* zaCLR)1*eLO4{P=G%BCu#KUZzGxS+3GG`AHl!@Bx_w|$RwvMX9vbkx;W@|IwA<5)GV zE5KYXDXwp}+H`n%m01{ABV$*0>J^>Xi_q6V{TQC{@-C2ds1tCX1NP~VtLt2cmx&pa<-0}JXE{7%w$XJ` zf8+*B9*RVkmuDcbr*c{3**b_yxfUnEMyKYK zF&}YT2T|`SyzRQuNwCpeUFXITW?baQ@!5H)M%>mx)T_n&*|Cghu=7kQ)$QN?;K%o~ zfJJ1qNx!iBEo<+I7=sG>TSmwVVz3DnD zuwIVsrd=mVq8ytIAC#{19ovB}XO{EGl@y0vsq1_fY^m$yJ2)G-en(mYNV+tK=6~)s zFyAU8YCP42#?LZ7yRmH#kMriL(_t@#kNOVtSZ{1@ThU%u$$gs$O*p70-9p4Vc$n%* zOA$uBg%%&oC^t%J@xcsQd@uvsG+lf!gBBmmK=Hv0yg~ED2Qz4F2o}CSSKO-j2gP?4 zQ-LWbKA2HYd@uv~s)79$AI!iSrO#00R~^j%uA=y01}#3Af#QQ1C_b2h;)59|KA3^x zgBd73n1Rc1Lm)r#!3-21%)r|&8f#QQ1_@t(b4`$G> zD=j{lLBp-A)Ds`fphqcvnBr7L@xhGzWlFayiVtR_iw|a?_+SQ#4`!hFU)+@^1d*q7`W}x_B28s`6p!i@0iVtR> z_+SQ#4`!hFU_+SQhsNPe2FoV8QY4O1f`ZlG-2Q%nj zC@nsiK_~D)<#@{#rzwgLW~5)F^j5_;72j19y(!jS3J);qJ4X{y?=xK2p!tmLi=OoV E0gsN{S^xk5 diff --git a/ports/cortex_a5/gnu/example_build/libgcc.a b/ports/cortex_a5/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 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 z 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 dhn|!HpOEmEh$!@SiRZoRqCc?z|Ecwv{|7^Je? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/arc_em/metaware/test_regression/threadx_regression/.project b/ports/cortex_a53/ac6/example_build/sample_threadx/.project similarity index 77% rename from ports/arc_em/metaware/test_regression/threadx_regression/.project rename to ports/cortex_a53/ac6/example_build/sample_threadx/.project index 3a0bbbec..a1b15572 100644 --- a/ports/arc_em/metaware/test_regression/threadx_regression/.project +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/.project @@ -1,6 +1,6 @@ - sample_threadx_regression + sample_threadx @@ -23,11 +23,4 @@ org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - - - Regression - 2 - PARENT-5-PROJECT_LOC/TX/Test/Regression - - diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a53/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a53/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..f55da65f --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a53/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a53/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a53/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a53/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a53/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a53/ac6/example_build/tx/.cproject b/ports/cortex_a53/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..35fed0f2 --- /dev/null +++ b/ports/cortex_a53/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/arc_em/metaware/test_validation/tx/.project b/ports/cortex_a53/ac6/example_build/tx/.project similarity index 100% rename from ports/arc_em/metaware/test_validation/tx/.project rename to ports/cortex_a53/ac6/example_build/tx/.project diff --git a/ports/cortex_a53/ac6/inc/tx_port.h b/ports/cortex_a53/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a53/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a53/ac6/src/tx_initialize_low_level.S b/ports/cortex_a53/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a53/ac6/src/tx_thread_context_restore.S b/ports/cortex_a53/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_context_save.S b/ports/cortex_a53/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a53/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a53/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a53/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a53/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a53/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a53/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a53/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_schedule.S b/ports/cortex_a53/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_stack_build.S b/ports/cortex_a53/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a53/ac6/src/tx_thread_system_return.S b/ports/cortex_a53/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a53/ac6/src/tx_timer_interrupt.S b/ports/cortex_a53/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a53/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a53/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/.project b/ports/cortex_a53/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a53/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a53/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..a2971cdf --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a53/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a53/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a53/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a53/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a53/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a53/gnu/example_build/tx/.cproject b/ports/cortex_a53/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a53/gnu/example_build/tx/.project b/ports/cortex_a53/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a53/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a53/gnu/inc/tx_port.h b/ports/cortex_a53/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a53/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a53/gnu/src/tx_initialize_low_level.S b/ports/cortex_a53/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_context_restore.S b/ports/cortex_a53/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_context_save.S b/ports/cortex_a53/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a53/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a53/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a53/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a53/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a53/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a53/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a53/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_schedule.S b/ports/cortex_a53/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_stack_build.S b/ports/cortex_a53/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a53/gnu/src/tx_thread_system_return.S b/ports/cortex_a53/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a53/gnu/src/tx_timer_interrupt.S b/ports/cortex_a53/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a53/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a55/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..f4e329dc --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/.project b/ports/cortex_a55/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a55/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a55/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..c5238ecd --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a55/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a55/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a55/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a55/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a55/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a55/ac6/example_build/tx/.cproject b/ports/cortex_a55/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..0d6f5a10 --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a55/ac6/example_build/tx/.project b/ports/cortex_a55/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a55/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a55/ac6/inc/tx_port.h b/ports/cortex_a55/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a55/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a55/ac6/src/tx_initialize_low_level.S b/ports/cortex_a55/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a55/ac6/src/tx_thread_context_restore.S b/ports/cortex_a55/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_context_save.S b/ports/cortex_a55/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a55/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a55/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a55/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a55/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a55/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a55/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a55/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_schedule.S b/ports/cortex_a55/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_stack_build.S b/ports/cortex_a55/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a55/ac6/src/tx_thread_system_return.S b/ports/cortex_a55/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a55/ac6/src/tx_timer_interrupt.S b/ports/cortex_a55/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a55/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a55/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/.project b/ports/cortex_a55/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a55/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a55/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..925ddb4c --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a55/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a55/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a55/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a55/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a55/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a55/gnu/example_build/tx/.cproject b/ports/cortex_a55/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a55/gnu/example_build/tx/.project b/ports/cortex_a55/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a55/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a55/gnu/inc/tx_port.h b/ports/cortex_a55/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a55/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a55/gnu/src/tx_initialize_low_level.S b/ports/cortex_a55/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_context_restore.S b/ports/cortex_a55/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_context_save.S b/ports/cortex_a55/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a55/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a55/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a55/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a55/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a55/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a55/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a55/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_schedule.S b/ports/cortex_a55/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_stack_build.S b/ports/cortex_a55/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a55/gnu/src/tx_thread_system_return.S b/ports/cortex_a55/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a55/gnu/src/tx_timer_interrupt.S b/ports/cortex_a55/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a55/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a57/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..d6254b98 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/.project b/ports/cortex_a57/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a57/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a57/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..afa62aef --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a57/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a57/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a57/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a57/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a57/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a57/ac6/example_build/tx/.cproject b/ports/cortex_a57/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..5ce4655a --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a57/ac6/example_build/tx/.project b/ports/cortex_a57/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a57/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a57/ac6/inc/tx_port.h b/ports/cortex_a57/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a57/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a57/ac6/src/tx_initialize_low_level.S b/ports/cortex_a57/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a57/ac6/src/tx_thread_context_restore.S b/ports/cortex_a57/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_context_save.S b/ports/cortex_a57/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a57/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a57/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a57/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a57/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a57/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a57/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a57/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_schedule.S b/ports/cortex_a57/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_stack_build.S b/ports/cortex_a57/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a57/ac6/src/tx_thread_system_return.S b/ports/cortex_a57/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a57/ac6/src/tx_timer_interrupt.S b/ports/cortex_a57/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a57/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a57/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/.project b/ports/cortex_a57/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a57/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a57/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..688d47e3 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a57/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a57/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a57/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a57/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a57/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a57/gnu/example_build/tx/.cproject b/ports/cortex_a57/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a57/gnu/example_build/tx/.project b/ports/cortex_a57/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a57/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a57/gnu/inc/tx_port.h b/ports/cortex_a57/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a57/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a57/gnu/src/tx_initialize_low_level.S b/ports/cortex_a57/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_context_restore.S b/ports/cortex_a57/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_context_save.S b/ports/cortex_a57/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a57/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a57/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a57/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a57/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a57/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a57/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a57/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_schedule.S b/ports/cortex_a57/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_stack_build.S b/ports/cortex_a57/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a57/gnu/src/tx_thread_system_return.S b/ports/cortex_a57/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a57/gnu/src/tx_timer_interrupt.S b/ports/cortex_a57/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a57/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a65/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..a74327d6 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/.project b/ports/cortex_a65/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a65/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a65/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..71866b51 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a65/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a65/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a65/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a65/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a65/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a65/ac6/example_build/tx/.cproject b/ports/cortex_a65/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..033932c0 --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65/ac6/example_build/tx/.project b/ports/cortex_a65/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a65/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a65/ac6/inc/tx_port.h b/ports/cortex_a65/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a65/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a65/ac6/src/tx_initialize_low_level.S b/ports/cortex_a65/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a65/ac6/src/tx_thread_context_restore.S b/ports/cortex_a65/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_context_save.S b/ports/cortex_a65/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a65/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a65/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a65/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a65/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a65/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a65/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a65/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_schedule.S b/ports/cortex_a65/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_stack_build.S b/ports/cortex_a65/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a65/ac6/src/tx_thread_system_return.S b/ports/cortex_a65/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a65/ac6/src/tx_timer_interrupt.S b/ports/cortex_a65/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a65/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a65/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/.project b/ports/cortex_a65/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a65/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a65/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..14f4ed2a --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a65/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a65/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a65/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a65/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a65/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a65/gnu/example_build/tx/.cproject b/ports/cortex_a65/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65/gnu/example_build/tx/.project b/ports/cortex_a65/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a65/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a65/gnu/inc/tx_port.h b/ports/cortex_a65/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a65/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a65/gnu/src/tx_initialize_low_level.S b/ports/cortex_a65/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_context_restore.S b/ports/cortex_a65/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_context_save.S b/ports/cortex_a65/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a65/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a65/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a65/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a65/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a65/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a65/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a65/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_schedule.S b/ports/cortex_a65/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_stack_build.S b/ports/cortex_a65/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a65/gnu/src/tx_thread_system_return.S b/ports/cortex_a65/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a65/gnu/src/tx_timer_interrupt.S b/ports/cortex_a65/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a65/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a65ae/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..d2474a55 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/.project b/ports/cortex_a65ae/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a65ae/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..711e1a98 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a65ae/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a65ae/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a65ae/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a65ae/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a65ae/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a65ae/ac6/example_build/tx/.cproject b/ports/cortex_a65ae/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..bbfb6933 --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65ae/ac6/example_build/tx/.project b/ports/cortex_a65ae/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a65ae/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a65ae/ac6/inc/tx_port.h b/ports/cortex_a65ae/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a65ae/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a65ae/ac6/src/tx_initialize_low_level.S b/ports/cortex_a65ae/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_context_restore.S b/ports/cortex_a65ae/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_context_save.S b/ports/cortex_a65ae/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a65ae/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a65ae/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_schedule.S b/ports/cortex_a65ae/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_stack_build.S b/ports/cortex_a65ae/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_thread_system_return.S b/ports/cortex_a65ae/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a65ae/ac6/src/tx_timer_interrupt.S b/ports/cortex_a65ae/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a65ae/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a65ae/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/.project b/ports/cortex_a65ae/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a65ae/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..ca99098e --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a65ae/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a65ae/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a65ae/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a65ae/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a65ae/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a65ae/gnu/example_build/tx/.cproject b/ports/cortex_a65ae/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a65ae/gnu/example_build/tx/.project b/ports/cortex_a65ae/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a65ae/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a65ae/gnu/inc/tx_port.h b/ports/cortex_a65ae/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a65ae/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a65ae/gnu/src/tx_initialize_low_level.S b/ports/cortex_a65ae/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_context_restore.S b/ports/cortex_a65ae/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_context_save.S b/ports/cortex_a65ae/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a65ae/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a65ae/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_schedule.S b/ports/cortex_a65ae/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_stack_build.S b/ports/cortex_a65ae/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_thread_system_return.S b/ports/cortex_a65ae/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a65ae/gnu/src/tx_timer_interrupt.S b/ports/cortex_a65ae/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a65ae/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a7/green/example_build/azure_rtos_workspace.gpj b/ports/cortex_a7/ghs/example_build/azure_rtos_workspace.gpj similarity index 100% rename from ports/cortex_a7/green/example_build/azure_rtos_workspace.gpj rename to ports/cortex_a7/ghs/example_build/azure_rtos_workspace.gpj diff --git a/ports/cortex_a7/green/example_build/reset.arm b/ports/cortex_a7/ghs/example_build/reset.arm similarity index 100% rename from ports/cortex_a7/green/example_build/reset.arm rename to ports/cortex_a7/ghs/example_build/reset.arm diff --git a/ports/cortex_a5/green/example_build/sample_threadx.c b/ports/cortex_a7/ghs/example_build/sample_threadx.c similarity index 93% rename from ports/cortex_a5/green/example_build/sample_threadx.c rename to ports/cortex_a7/ghs/example_build/sample_threadx.c index 418ec634..8c61de06 100644 --- a/ports/cortex_a5/green/example_build/sample_threadx.c +++ b/ports/cortex_a7/ghs/example_build/sample_threadx.c @@ -1,5 +1,5 @@ /* 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, + threads of different priorities, using a message queue, semaphore, mutex, event flags group, byte pool, and block pool. */ #include "tx_api.h" @@ -80,42 +80,42 @@ CHAR *pointer = TX_NULL; 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, + 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 + /* 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, + 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, + 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. + /* 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, + 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, + 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. */ @@ -123,23 +123,23 @@ CHAR *pointer = TX_NULL; /* 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, + 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, + 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, + 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. */ @@ -242,11 +242,11 @@ UINT status; /* 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 + /* 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++; } @@ -305,7 +305,7 @@ ULONG actual_flags; thread_5_counter++; /* Wait for event flag 0. */ - status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); /* Check status. */ @@ -358,7 +358,7 @@ UINT status; if (status != TX_SUCCESS) break; - /* Release the mutex again. This will actually + /* Release the mutex again. This will actually release ownership since it was obtained twice. */ status = tx_mutex_put(&mutex_0); diff --git a/ports/cortex_a7/green/example_build/sample_threadx.con b/ports/cortex_a7/ghs/example_build/sample_threadx.con similarity index 87% rename from ports/cortex_a7/green/example_build/sample_threadx.con rename to ports/cortex_a7/ghs/example_build/sample_threadx.con index 70f51196..88c28168 100644 --- a/ports/cortex_a7/green/example_build/sample_threadx.con +++ b/ports/cortex_a7/ghs/example_build/sample_threadx.con @@ -1,4 +1,4 @@ -target_connection.00000000.title="Simulator connection for ThreadX" +target_connection.00000000.title="Simulator" target_connection.00000000.type="Custom" target_connection.00000000.short_type="Custom" target_connection.00000000.args="simarm -cpu=cortexa7 -fpu -rom" diff --git a/ports/cortex_a7/green/example_build/sample_threadx.gpj b/ports/cortex_a7/ghs/example_build/sample_threadx.gpj similarity index 100% rename from ports/cortex_a7/green/example_build/sample_threadx.gpj rename to ports/cortex_a7/ghs/example_build/sample_threadx.gpj diff --git a/ports/cortex_a7/green/example_build/sample_threadx.ld b/ports/cortex_a7/ghs/example_build/sample_threadx.ld similarity index 81% rename from ports/cortex_a7/green/example_build/sample_threadx.ld rename to ports/cortex_a7/ghs/example_build/sample_threadx.ld index 8d1ab4df..a93f11ea 100644 --- a/ports/cortex_a7/green/example_build/sample_threadx.ld +++ b/ports/cortex_a7/ghs/example_build/sample_threadx.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x10000) : .stack align(16) pad(0x1000) : .free_mem align(16) pad(0x10000) : diff --git a/ports/cortex_a7/green/example_build/sample_threadx_el.gpj b/ports/cortex_a7/ghs/example_build/sample_threadx_el.gpj similarity index 100% rename from ports/cortex_a7/green/example_build/sample_threadx_el.gpj rename to ports/cortex_a7/ghs/example_build/sample_threadx_el.gpj diff --git a/ports/cortex_a8/green/example_build/sample_threadx_el.ld b/ports/cortex_a7/ghs/example_build/sample_threadx_el.ld similarity index 82% rename from ports/cortex_a8/green/example_build/sample_threadx_el.ld rename to ports/cortex_a7/ghs/example_build/sample_threadx_el.ld index 33c0f934..65d9de03 100644 --- a/ports/cortex_a8/green/example_build/sample_threadx_el.ld +++ b/ports/cortex_a7/ghs/example_build/sample_threadx_el.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x1000) : .stack align(16) pad(0x1000) : .eventlog align(16) pad(0x10000) : diff --git a/ports/cortex_a5/green/example_build/tx.gpj b/ports/cortex_a7/ghs/example_build/tx.gpj similarity index 72% rename from ports/cortex_a5/green/example_build/tx.gpj rename to ports/cortex_a7/ghs/example_build/tx.gpj index afbd6bef..a8a7bbb8 100644 --- a/ports/cortex_a5/green/example_build/tx.gpj +++ b/ports/cortex_a7/ghs/example_build/tx.gpj @@ -1,7 +1,6 @@ #!gbuild [Library] -I../../../../common/inc - -I../../../../ports_common_green/inc -I../inc ..\..\..\..\common\inc\tx_api.h ..\..\..\..\common\inc\tx_block_pool.h @@ -16,8 +15,11 @@ ..\..\..\..\common\inc\tx_trace.h ..\..\..\..\common\inc\tx_user_sample.h ..\inc\tx_port.h -..\..\..\..\ports_common_green\inc\tx_el.h -..\..\..\..\ports_common_green\inc\tx_ghs.h +..\inc\tx_el.h +..\inc\tx_ghs.h +..\src\tx_el.c +..\src\tx_ghs.c +..\src\tx_ghse.c ..\src\tx_thread_context_restore.arm ..\src\tx_thread_context_save.arm ..\src\tx_thread_fiq_context_restore.arm @@ -218,66 +220,3 @@ ..\..\..\..\common\src\txe_timer_deactivate.c ..\..\..\..\common\src\txe_timer_delete.c ..\..\..\..\common\src\txe_timer_info_get.c -..\..\..\..\ports_common_green\src\tx_el.c -..\..\..\..\ports_common_green\src\tx_ghs.c -..\..\..\..\ports_common_green\src\tx_ghse.c -..\..\..\..\ports_common_green\src\txr_block_allocate.c -..\..\..\..\ports_common_green\src\txr_block_pool_create.c -..\..\..\..\ports_common_green\src\txr_block_pool_delete.c -..\..\..\..\ports_common_green\src\txr_block_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_block_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_block_release.c -..\..\..\..\ports_common_green\src\txr_byte_allocate.c -..\..\..\..\ports_common_green\src\txr_byte_pool_create.c -..\..\..\..\ports_common_green\src\txr_byte_pool_delete.c -..\..\..\..\ports_common_green\src\txr_byte_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_byte_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_byte_release.c -..\..\..\..\ports_common_green\src\txr_event_flags_create.c -..\..\..\..\ports_common_green\src\txr_event_flags_delete.c -..\..\..\..\ports_common_green\src\txr_event_flags_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_info_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_set.c -..\..\..\..\ports_common_green\src\txr_event_flags_set_notify.c -..\..\..\..\ports_common_green\src\txr_ghs.c -..\..\..\..\ports_common_green\src\txr_mutex_create.c -..\..\..\..\ports_common_green\src\txr_mutex_delete.c -..\..\..\..\ports_common_green\src\txr_mutex_get.c -..\..\..\..\ports_common_green\src\txr_mutex_info_get.c -..\..\..\..\ports_common_green\src\txr_mutex_prioritize.c -..\..\..\..\ports_common_green\src\txr_mutex_put.c -..\..\..\..\ports_common_green\src\txr_queue_create.c -..\..\..\..\ports_common_green\src\txr_queue_delete.c -..\..\..\..\ports_common_green\src\txr_queue_flush.c -..\..\..\..\ports_common_green\src\txr_queue_front_send.c -..\..\..\..\ports_common_green\src\txr_queue_info_get.c -..\..\..\..\ports_common_green\src\txr_queue_prioritize.c -..\..\..\..\ports_common_green\src\txr_queue_receive.c -..\..\..\..\ports_common_green\src\txr_queue_send.c -..\..\..\..\ports_common_green\src\txr_queue_send_notify.c -..\..\..\..\ports_common_green\src\txr_semaphore_ceiling_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_create.c -..\..\..\..\ports_common_green\src\txr_semaphore_delete.c -..\..\..\..\ports_common_green\src\txr_semaphore_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_info_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_prioritize.c -..\..\..\..\ports_common_green\src\txr_semaphore_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_put_notify.c -..\..\..\..\ports_common_green\src\txr_thread_create.c -..\..\..\..\ports_common_green\src\txr_thread_delete.c -..\..\..\..\ports_common_green\src\txr_thread_entry_exit_notify.c -..\..\..\..\ports_common_green\src\txr_thread_info_get.c -..\..\..\..\ports_common_green\src\txr_thread_preemption_change.c -..\..\..\..\ports_common_green\src\txr_thread_priority_change.c -..\..\..\..\ports_common_green\src\txr_thread_reset.c -..\..\..\..\ports_common_green\src\txr_thread_resume.c -..\..\..\..\ports_common_green\src\txr_thread_suspend.c -..\..\..\..\ports_common_green\src\txr_thread_terminate.c -..\..\..\..\ports_common_green\src\txr_thread_time_slice_change.c -..\..\..\..\ports_common_green\src\txr_thread_wait_abort.c -..\..\..\..\ports_common_green\src\txr_timer_activate.c -..\..\..\..\ports_common_green\src\txr_timer_change.c -..\..\..\..\ports_common_green\src\txr_timer_create.c -..\..\..\..\ports_common_green\src\txr_timer_deactivate.c -..\..\..\..\ports_common_green\src\txr_timer_delete.c -..\..\..\..\ports_common_green\src\txr_timer_info_get.c diff --git a/ports/cortex_a9/green/example_build/tx_initialize_low_level.arm b/ports/cortex_a7/ghs/example_build/tx_initialize_low_level.arm similarity index 94% rename from ports/cortex_a9/green/example_build/tx_initialize_low_level.arm rename to ports/cortex_a7/ghs/example_build/tx_initialize_low_level.arm index 604586e2..24739a35 100644 --- a/ports/cortex_a9/green/example_build/tx_initialize_low_level.arm +++ b/ports/cortex_a7/ghs/example_build/tx_initialize_low_level.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Initialize */ /** */ @@ -42,42 +42,42 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-A7/GHS */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, 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 */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -96,7 +96,7 @@ _tx_initialize_low_level: /* _tx_thread_system_stack_ptr = (VOID_PTR) (sp); */ LDR r1,=_tx_thread_system_stack_ptr # Pickup address of system stack ptr - STR sp, [r1] # Save system stack + STR sp, [r1] # Save system stack /* Pickup the first available memory address. */ @@ -146,8 +146,8 @@ _tx_initialize_low_level: STR r0, [r2] # Save first free memory address - /* Setup Timer for periodic interrupts. To generate timer interrupts with - the Green Hills simulator, enter the following command in the target + /* Setup Timer for periodic interrupts. To generate timer interrupts with + the Green Hills simulator, enter the following command in the target window: timer 9999 irq */ /* Done, return to caller. */ @@ -197,7 +197,7 @@ __tx_reserved_handler: .size __tx_reserved_handler,.-__tx_reserved_handler .globl __tx_irq_handler - .globl __tx_irq_processing_return + .globl __tx_irq_processing_return __tx_irq_handler: /* Jump to context save to save system context. */ @@ -209,18 +209,18 @@ __tx_irq_handler: __tx_irq_processing_return: /* At this point execution is still in the IRQ mode. The CPSR, point of - interrupt, and all C scratch registers are available for use. */ + interrupt, and all C scratch registers are available for use. */ #ifdef TX_ENABLE_EVENT_LOGGING MOV r0, 0 # Build interrupt code BL _tx_el_interrupt # Call interrupt event logging #endif - /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start + /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start from IRQ mode with interrupts disabled. This routine switches to the - system mode and returns with IRQ interrupts enabled. - - NOTE: It is very important to ensure all IRQ interrupts are cleared + system mode and returns with IRQ interrupts enabled. + + NOTE: It is very important to ensure all IRQ interrupts are cleared prior to enabling nested IRQ interrupts. */ #ifdef TX_ENABLE_IRQ_NESTING BL _tx_thread_irq_nesting_start @@ -235,7 +235,7 @@ __tx_irq_processing_return: /* Application IRQ handlers can be called here! */ /* If interrupt nesting was started earlier, the end of interrupt nesting - service must be called before returning to _tx_thread_context_restore. + service must be called before returning to _tx_thread_context_restore. This routine returns in processing in IRQ mode with interrupts disabled. */ #ifdef TX_ENABLE_IRQ_NESTING BL _tx_thread_irq_nesting_end @@ -272,12 +272,12 @@ __tx_fiq_processing_return: MOV r0, 1 # Build interrupt code BL _tx_el_interrupt # Call interrupt event logging #endif - - /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start + + /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start from FIQ mode with interrupts disabled. This routine switches to the system mode and returns with FIQ interrupts enabled. - NOTE: It is very important to ensure all FIQ interrupts are cleared + NOTE: It is very important to ensure all FIQ interrupts are cleared prior to enabling nested FIQ interrupts. */ #ifdef TX_ENABLE_FIQ_NESTING BL _tx_thread_fiq_nesting_start diff --git a/ports/cortex_a8/green/example_build/txe.gpj b/ports/cortex_a7/ghs/example_build/txe.gpj similarity index 72% rename from ports/cortex_a8/green/example_build/txe.gpj rename to ports/cortex_a7/ghs/example_build/txe.gpj index 662bcd16..302c0cb2 100644 --- a/ports/cortex_a8/green/example_build/txe.gpj +++ b/ports/cortex_a7/ghs/example_build/txe.gpj @@ -2,7 +2,6 @@ [Library] -DTX_ENABLE_EVENT_LOGGING -I../../../../common/inc - -I../../../../ports_common_green/inc -I../inc ..\..\..\..\common\inc\tx_api.h ..\..\..\..\common\inc\tx_block_pool.h @@ -17,8 +16,11 @@ ..\..\..\..\common\inc\tx_trace.h ..\..\..\..\common\inc\tx_user_sample.h ..\inc\tx_port.h -..\..\..\..\ports_common_green\inc\tx_el.h -..\..\..\..\ports_common_green\inc\tx_ghs.h +..\inc\tx_el.h +..\inc\tx_ghs.h +..\src\tx_el.c +..\src\tx_ghs.c +..\src\tx_ghse.c ..\src\tx_thread_context_restore.arm ..\src\tx_thread_context_save.arm ..\src\tx_thread_fiq_context_restore.arm @@ -219,66 +221,3 @@ ..\..\..\..\common\src\txe_timer_deactivate.c ..\..\..\..\common\src\txe_timer_delete.c ..\..\..\..\common\src\txe_timer_info_get.c -..\..\..\..\ports_common_green\src\tx_el.c -..\..\..\..\ports_common_green\src\tx_ghs.c -..\..\..\..\ports_common_green\src\tx_ghse.c -..\..\..\..\ports_common_green\src\txr_block_allocate.c -..\..\..\..\ports_common_green\src\txr_block_pool_create.c -..\..\..\..\ports_common_green\src\txr_block_pool_delete.c -..\..\..\..\ports_common_green\src\txr_block_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_block_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_block_release.c -..\..\..\..\ports_common_green\src\txr_byte_allocate.c -..\..\..\..\ports_common_green\src\txr_byte_pool_create.c -..\..\..\..\ports_common_green\src\txr_byte_pool_delete.c -..\..\..\..\ports_common_green\src\txr_byte_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_byte_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_byte_release.c -..\..\..\..\ports_common_green\src\txr_event_flags_create.c -..\..\..\..\ports_common_green\src\txr_event_flags_delete.c -..\..\..\..\ports_common_green\src\txr_event_flags_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_info_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_set.c -..\..\..\..\ports_common_green\src\txr_event_flags_set_notify.c -..\..\..\..\ports_common_green\src\txr_ghs.c -..\..\..\..\ports_common_green\src\txr_mutex_create.c -..\..\..\..\ports_common_green\src\txr_mutex_delete.c -..\..\..\..\ports_common_green\src\txr_mutex_get.c -..\..\..\..\ports_common_green\src\txr_mutex_info_get.c -..\..\..\..\ports_common_green\src\txr_mutex_prioritize.c -..\..\..\..\ports_common_green\src\txr_mutex_put.c -..\..\..\..\ports_common_green\src\txr_queue_create.c -..\..\..\..\ports_common_green\src\txr_queue_delete.c -..\..\..\..\ports_common_green\src\txr_queue_flush.c -..\..\..\..\ports_common_green\src\txr_queue_front_send.c -..\..\..\..\ports_common_green\src\txr_queue_info_get.c -..\..\..\..\ports_common_green\src\txr_queue_prioritize.c -..\..\..\..\ports_common_green\src\txr_queue_receive.c -..\..\..\..\ports_common_green\src\txr_queue_send.c -..\..\..\..\ports_common_green\src\txr_queue_send_notify.c -..\..\..\..\ports_common_green\src\txr_semaphore_ceiling_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_create.c -..\..\..\..\ports_common_green\src\txr_semaphore_delete.c -..\..\..\..\ports_common_green\src\txr_semaphore_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_info_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_prioritize.c -..\..\..\..\ports_common_green\src\txr_semaphore_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_put_notify.c -..\..\..\..\ports_common_green\src\txr_thread_create.c -..\..\..\..\ports_common_green\src\txr_thread_delete.c -..\..\..\..\ports_common_green\src\txr_thread_entry_exit_notify.c -..\..\..\..\ports_common_green\src\txr_thread_info_get.c -..\..\..\..\ports_common_green\src\txr_thread_preemption_change.c -..\..\..\..\ports_common_green\src\txr_thread_priority_change.c -..\..\..\..\ports_common_green\src\txr_thread_reset.c -..\..\..\..\ports_common_green\src\txr_thread_resume.c -..\..\..\..\ports_common_green\src\txr_thread_suspend.c -..\..\..\..\ports_common_green\src\txr_thread_terminate.c -..\..\..\..\ports_common_green\src\txr_thread_time_slice_change.c -..\..\..\..\ports_common_green\src\txr_thread_wait_abort.c -..\..\..\..\ports_common_green\src\txr_timer_activate.c -..\..\..\..\ports_common_green\src\txr_timer_change.c -..\..\..\..\ports_common_green\src\txr_timer_create.c -..\..\..\..\ports_common_green\src\txr_timer_deactivate.c -..\..\..\..\ports_common_green\src\txr_timer_delete.c -..\..\..\..\ports_common_green\src\txr_timer_info_get.c diff --git a/ports/cortex_a7/ghs/inc/tx_el.h b/ports/cortex_a7/ghs/inc/tx_el.h new file mode 100644 index 00000000..66cc0d7c --- /dev/null +++ b/ports/cortex_a7/ghs/inc/tx_el.h @@ -0,0 +1,765 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* tx_el.h PORTABLE C/GHS */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the ThreadX event log functions for the GHS MULTI */ +/* EventAnalyzer. It is assumed that tx_api.h and tx_port.h have */ +/* already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef TX_EL_H +#define TX_EL_H + + +/* Define Event Log specific data definitions. */ + +#define TX_EL_VERSION_ID 2 /* Event log version ID */ +#define TX_EL_HEADER_SIZE 24 /* Event log header size */ +#define TX_EL_TNIS 16 /* Number of thread names supported */ + /* If the application needs to */ + /* track more thread names, just */ + /* increase this number and re- */ + /* build the ThreadX library. */ +#define TX_EL_TNI_ENTRY_SIZE 44 /* Thread name entries are 44 bytes */ +#define TX_EL_TNI_NAME_SIZE 34 /* Thread name size in TNI */ +#define TX_EL_NO_MORE_TNI_ROOM 1 /* Error return from thread register*/ +#define TX_EL_NAME_NOT_FOUND 2 /* Error return from un-register */ +#define TX_EL_EVENT_SIZE 32 /* Number of bytes in each event */ +#define TX_EL_VALID_ENTRY 1 /* Valid log entry */ +#define TX_EL_INVALID_ENTRY 0 /* Invalid log entry */ + + +/* Define necessary offsets. */ + +#define TX_EL_TNI_VALID_OFFSET 34 +#define TX_EL_TNI_THREAD_ID_OFFSET 36 +#define TX_EL_TNI_THREAD_PRIORITY_OFF 40 +#define TX_EL_EVENT_TYPE_OFFSET 0 +#define TX_EL_EVENT_SUBTYPE_OFFSET 2 +#define TX_EL_EVENT_TIME_UPPER_OFFSET 4 +#define TX_EL_EVENT_TIME_LOWER_OFFSET 8 +#define TX_EL_EVENT_THREAD_OFFSET 12 +#define TX_EL_EVENT_INFO_1_OFFSET 16 +#define TX_EL_EVENT_INFO_2_OFFSET 20 +#define TX_EL_EVENT_INFO_3_OFFSET 24 +#define TX_EL_EVENT_INFO_4_OFFSET 28 + + +/* Undefine constants that might be been defined previously by tx_api.h. */ + +#undef TX_EL_INITIALIZE +#undef TX_EL_THREAD_REGISTER +#undef TX_EL_THREAD_UNREGISTER +#undef TX_EL_THREAD_STATUS_CHANGE_INSERT +#undef TX_EL_BYTE_ALLOCATE_INSERT +#undef TX_EL_BYTE_POOL_CREATE_INSERT +#undef TX_EL_BYTE_POOL_DELETE_INSERT +#undef TX_EL_BYTE_RELEASE_INSERT +#undef TX_EL_BLOCK_ALLOCATE_INSERT +#undef TX_EL_BLOCK_POOL_CREATE_INSERT +#undef TX_EL_BLOCK_POOL_DELETE_INSERT +#undef TX_EL_BLOCK_RELEASE_INSERT +#undef TX_EL_EVENT_FLAGS_CREATE_INSERT +#undef TX_EL_EVENT_FLAGS_DELETE_INSERT +#undef TX_EL_EVENT_FLAGS_GET_INSERT +#undef TX_EL_EVENT_FLAGS_SET_INSERT +#undef TX_EL_INTERRUPT_CONTROL_INSERT +#undef TX_EL_QUEUE_CREATE_INSERT +#undef TX_EL_QUEUE_DELETE_INSERT +#undef TX_EL_QUEUE_FLUSH_INSERT +#undef TX_EL_QUEUE_RECEIVE_INSERT +#undef TX_EL_QUEUE_SEND_INSERT +#undef TX_EL_SEMAPHORE_CREATE_INSERT +#undef TX_EL_SEMAPHORE_DELETE_INSERT +#undef TX_EL_SEMAPHORE_GET_INSERT +#undef TX_EL_SEMAPHORE_PUT_INSERT +#undef TX_EL_THREAD_CREATE_INSERT +#undef TX_EL_THREAD_DELETE_INSERT +#undef TX_EL_THREAD_IDENTIFY_INSERT +#undef TX_EL_THREAD_PREEMPTION_CHANGE_INSERT +#undef TX_EL_THREAD_PRIORITY_CHANGE_INSERT +#undef TX_EL_THREAD_RELINQUISH_INSERT +#undef TX_EL_THREAD_RESUME_INSERT +#undef TX_EL_THREAD_SLEEP_INSERT +#undef TX_EL_THREAD_SUSPEND_INSERT +#undef TX_EL_THREAD_TERMINATE_INSERT +#undef TX_EL_THREAD_TIME_SLICE_CHANGE_INSERT +#undef TX_EL_TIME_GET_INSERT +#undef TX_EL_TIME_SET_INSERT +#undef TX_EL_TIMER_ACTIVATE_INSERT +#undef TX_EL_TIMER_CHANGE_INSERT +#undef TX_EL_TIMER_CREATE_INSERT +#undef TX_EL_TIMER_DEACTIVATE_INSERT +#undef TX_EL_TIMER_DELETE_INSERT +#undef TX_EL_BLOCK_POOL_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PRIORITIZE_INSERT +#undef TX_EL_BYTE_POOL_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PRIORITIZE_INSERT +#undef TX_EL_EVENT_FLAGS_INFO_GET_INSERT +#undef TX_EL_MUTEX_CREATE_INSERT +#undef TX_EL_MUTEX_DELETE_INSERT +#undef TX_EL_MUTEX_GET_INSERT +#undef TX_EL_MUTEX_INFO_GET_INSERT +#undef TX_EL_MUTEX_PRIORITIZE_INSERT +#undef TX_EL_MUTEX_PUT_INSERT +#undef TX_EL_QUEUE_INFO_GET_INSERT +#undef TX_EL_QUEUE_FRONT_SEND_INSERT +#undef TX_EL_QUEUE_PRIORITIZE_INSERT +#undef TX_EL_SEMAPHORE_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PRIORITIZE_INSERT +#undef TX_EL_THREAD_INFO_GET_INSERT +#undef TX_EL_THREAD_WAIT_ABORT_INSERT +#undef TX_EL_TIMER_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT +#undef TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_QUEUE_SEND_NOTIFY_INSERT +#undef TX_EL_SEMAPHORE_CEILING_PUT_INSERT +#undef TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT +#undef TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT +#undef TX_EL_THREAD_RESET_INSERT +#undef TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT +#undef TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT + + +/* Define Event Types. */ + +#define TX_EL_THREAD_CHANGE 1 +#define TX_EL_INTERRUPT 2 +#define TX_EL_THREADX_CALL 3 +#define TX_EL_USER_EVENT 4 +#define TX_EL_THREAD_STATUS_CHANGE 5 +#define TX_EL_REFRESH 6 /* Not implemented */ +#define TX_EL_TIMER 7 /* Not implemented */ +#define TX_EL_TIMESOURCE_DELTA 8 /* Not implemented */ + + +/* Define TX_EL_THREADX_CALL event sub-types. */ + +#define TX_EL_BYTE_ALLOCATE 0 +#define TX_EL_BYTE_POOL_CREATE 1 +#define TX_EL_BYTE_POOL_DELETE 2 +#define TX_EL_BYTE_RELEASE 3 +#define TX_EL_BLOCK_ALLOCATE 4 +#define TX_EL_BLOCK_POOL_CREATE 5 +#define TX_EL_BLOCK_POOL_DELETE 6 +#define TX_EL_BLOCK_RELEASE 7 +#define TX_EL_EVENT_FLAGS_CREATE 8 +#define TX_EL_EVENT_FLAGS_DELETE 9 +#define TX_EL_EVENT_FLAGS_GET 10 +#define TX_EL_EVENT_FLAGS_SET 11 +#define TX_EL_INTERRUPT_CONTROL 12 +#define TX_EL_QUEUE_CREATE 13 +#define TX_EL_QUEUE_DELETE 14 +#define TX_EL_QUEUE_FLUSH 15 +#define TX_EL_QUEUE_RECEIVE 16 +#define TX_EL_QUEUE_SEND 17 +#define TX_EL_SEMAPHORE_CREATE 18 +#define TX_EL_SEMAPHORE_DELETE 19 +#define TX_EL_SEMAPHORE_GET 20 +#define TX_EL_SEMAPHORE_PUT 21 +#define TX_EL_THREAD_CREATE 22 +#define TX_EL_THREAD_DELETE 23 +#define TX_EL_THREAD_IDENTIFY 24 +#define TX_EL_THREAD_PREEMPTION_CHANGE 25 +#define TX_EL_THREAD_PRIORITY_CHANGE 26 +#define TX_EL_THREAD_RELINQUISH 27 +#define TX_EL_THREAD_RESUME 28 +#define TX_EL_THREAD_SLEEP 29 +#define TX_EL_THREAD_SUSPEND 30 +#define TX_EL_THREAD_TERMINATE 31 +#define TX_EL_THREAD_TIME_SLICE_CHANGE 32 +#define TX_EL_TIME_GET 33 +#define TX_EL_TIME_SET 34 +#define TX_EL_TIMER_ACTIVATE 35 +#define TX_EL_TIMER_CHANGE 36 +#define TX_EL_TIMER_CREATE 37 +#define TX_EL_TIMER_DEACTIVATE 38 +#define TX_EL_TIMER_DELETE 39 +#define TX_EL_BLOCK_POOL_INFO_GET 40 +#define TX_EL_BLOCK_POOL_PRIORITIZE 41 +#define TX_EL_BYTE_POOL_INFO_GET 42 +#define TX_EL_BYTE_POOL_PRIORITIZE 43 +#define TX_EL_EVENT_FLAGS_INFO_GET 44 +#define TX_EL_MUTEX_CREATE 45 +#define TX_EL_MUTEX_DELETE 46 +#define TX_EL_MUTEX_GET 47 +#define TX_EL_MUTEX_INFO_GET 48 +#define TX_EL_MUTEX_PRIORITIZE 49 +#define TX_EL_MUTEX_PUT 50 +#define TX_EL_QUEUE_INFO_GET 51 +#define TX_EL_QUEUE_FRONT_SEND 52 +#define TX_EL_QUEUE_PRIORITIZE 53 +#define TX_EL_SEMAPHORE_INFO_GET 54 +#define TX_EL_SEMAPHORE_PRIORITIZE 55 +#define TX_EL_THREAD_INFO_GET 56 +#define TX_EL_THREAD_WAIT_ABORT 57 +#define TX_EL_TIMER_INFO_GET 58 +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET 59 +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET 60 +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET 61 +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET 62 +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET 63 +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET 64 +#define TX_EL_EVENT_FLAGS_SET_NOTIFY 65 +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET 66 +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET 67 +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET 68 +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET 69 +#define TX_EL_QUEUE_SEND_NOTIFY 70 +#define TX_EL_SEMAPHORE_CEILING_PUT 71 +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET 72 +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET 73 +#define TX_EL_SEMAPHORE_PUT_NOTIFY 74 +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY 75 +#define TX_EL_THREAD_RESET 76 +#define TX_EL_THREAD_PERFORMANCE_INFO_GET 77 +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET 78 +#define TX_EL_THREAD_STACK_ERROR_NOTIFY 79 +#define TX_EL_TIMER_PERFORMANCE_INFO_GET 80 +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET 81 + + +/* Define ThreadX sub-types. */ + +#define TX_EL_INTERRUPT_SUB_TYPE 1 +#define TX_EL_END_OF_INTERRUPT 3 + + +/* Define event logging filters, which may be used by the application program to + dynamically enable/disable events in run-time. */ + +#define TX_EL_FILTER_STATUS_CHANGE 0x0001 +#define TX_EL_FILTER_INTERRUPTS 0x0002 +#define TX_EL_FILTER_THREAD_CALLS 0x0004 +#define TX_EL_FILTER_TIMER_CALLS 0x0008 +#define TX_EL_FILTER_EVENT_FLAG_CALLS 0x0010 +#define TX_EL_FILTER_SEMAPHORE_CALLS 0x0020 +#define TX_EL_FILTER_QUEUE_CALLS 0x0040 +#define TX_EL_FILTER_BLOCK_CALLS 0x0080 +#define TX_EL_FILTER_BYTE_CALLS 0x0100 +#define TX_EL_FILTER_MUTEX_CALLS 0x0200 +#define TX_EL_FILTER_ALL_EVENTS 0xFFFF +#define TX_EL_ENABLE_ALL_EVENTS 0x0000 + + +/* Define filter macros that are inserted in-line with the other macros below. */ + +#ifdef TX_ENABLE_EVENT_FILTERS +#define TX_EL_NO_STATUS_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_STATUS_CHANGE)) { +#define TX_EL_NO_INTERRUPT_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_INTERRUPTS)) { +#define TX_EL_NO_THREAD_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_THREAD_CALLS)) { +#define TX_EL_NO_TIMER_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_TIMER_CALLS)) { +#define TX_EL_NO_EVENT_FLAG_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_EVENT_FLAG_CALLS)) { +#define TX_EL_NO_SEMAPHORE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_SEMAPHORE_CALLS)) { +#define TX_EL_NO_QUEUE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_QUEUE_CALLS)) { +#define TX_EL_NO_BLOCK_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_BLOCK_CALLS)) { +#define TX_EL_NO_BYTE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_BYTE_CALLS)) { +#define TX_EL_NO_MUTEX_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_MUTEX_CALLS)) { +#define TX_EL_END_FILTER } +#else +#define TX_EL_NO_STATUS_EVENTS +#define TX_EL_NO_INTERRUPT_EVENTS +#define TX_EL_NO_THREAD_EVENTS +#define TX_EL_NO_TIMER_EVENTS +#define TX_EL_NO_EVENT_FLAG_EVENTS +#define TX_EL_NO_SEMAPHORE_EVENTS +#define TX_EL_NO_QUEUE_EVENTS +#define TX_EL_NO_BLOCK_EVENTS +#define TX_EL_NO_BYTE_EVENTS +#define TX_EL_NO_MUTEX_EVENTS +#define TX_EL_END_FILTER +#endif + +/* Define externs and constants for non-event log source modules. This is for + the in-line macros below. */ + +#ifndef TX_EL_SOURCE_CODE +extern UCHAR *_tx_el_tni_start; +extern UCHAR **_tx_el_current_event; +extern UCHAR *_tx_el_event_area_start; +extern UCHAR *_tx_el_event_area_end; +extern UINT _tx_el_maximum_events; +extern ULONG _tx_el_total_events; +extern TX_THREAD *_tx_thread_current_ptr; +extern UINT _tx_el_event_filter; +extern ULONG _tx_el_time_base_upper; +extern ULONG _tx_el_time_base_lower; + + +/* Define macros for event logging functions. */ + +#define TX_EL_THREAD_CREATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_THREAD_CREATE, thread_ptr, stack_start, stack_size, priority); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_SET, group_ptr, flags_to_set, set_option); TX_EL_END_FILTER +#define TX_EL_THREAD_DELETE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_DELETE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_TIME_SLICE_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_TIME_SLICE_CHANGE, thread_ptr, thread_ptr -> tx_thread_new_time_slice, new_time_slice); TX_EL_END_FILTER +#define TX_EL_THREAD_TERMINATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_TERMINATE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_SLEEP_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SLEEP, timer_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_SUSPEND_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SUSPEND, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_RELINQUISH_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_RELINQUISH); TX_EL_END_FILTER +#define TX_EL_THREAD_RESUME_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESUME, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PRIORITY_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PRIORITY_CHANGE, thread_ptr, thread_ptr -> tx_thread_priority, new_priority); TX_EL_END_FILTER +#define TX_EL_THREAD_PREEMPTION_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PREEMPTION_CHANGE, thread_ptr, thread_ptr -> tx_thread_preempt_threshold, new_threshold); TX_EL_END_FILTER +#define TX_EL_THREAD_WAIT_ABORT_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_WAIT_ABORT, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_THREAD_ENTRY_EXIT_NOTIFY, thread_ptr, thread_entry_exit_notify); TX_EL_END_FILTER +#define TX_EL_THREAD_RESET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_PERFORMANCE_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_STACK_ERROR_NOTIFY, stack_error_handler); TX_EL_END_FILTER +#define TX_EL_TIME_SET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_SET, new_time); TX_EL_END_FILTER +#define TX_EL_TIME_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_GET, _tx_timer_system_clock); TX_EL_END_FILTER +#define TX_EL_TIMER_DELETE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DELETE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_CREATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate); TX_EL_END_FILTER +#define TX_EL_TIMER_CHANGE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_TIMER_CHANGE, timer_ptr, initial_ticks, reschedule_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_IDENTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_IDENTIFY); TX_EL_END_FILTER +#define TX_EL_TIMER_DEACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DEACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_ACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_ACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_PERFORMANCE_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_GET, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_DELETE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_DELETE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CREATE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_CREATE, semaphore_ptr, initial_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PRIORITIZE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PRIORITIZE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CEILING_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_SEMAPHORE_CEILING_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, ceiling); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT_NOTIFY, semaphore_ptr, semaphore_put_notify); TX_EL_END_FILTER +#define TX_EL_QUEUE_FRONT_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_FRONT_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_RECEIVE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_RECEIVE, queue_ptr, destination_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_FLUSH_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_FLUSH, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_DELETE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_DELETE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_CREATE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_QUEUE_CREATE, queue_ptr, queue_start, queue_size, message_size); TX_EL_END_FILTER +#define TX_EL_QUEUE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PRIORITIZE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PRIORITIZE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PERFORMANCE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_NOTIFY_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND_NOTIFY, queue_ptr, queue_send_notify); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_GET, group_ptr, requested_flags, get_option); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_DELETE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_DELETE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_CREATE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_CREATE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_EVENT_FLAGS_SET_NOTIFY, group_ptr, events_set_notify); TX_EL_END_FILTER +#define TX_EL_BYTE_RELEASE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BYTE_RELEASE, pool_ptr, memory_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_DELETE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_CREATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_POOL_CREATE, pool_ptr, pool_start, pool_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PRIORITIZE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_ALLOCATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_ALLOCATE, pool_ptr, memory_ptr, memory_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_BLOCK_RELEASE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_RELEASE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_DELETE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_CREATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_BLOCK_POOL_CREATE, pool_ptr, pool_start, pool_size, block_size); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PRIORITIZE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_ALLOCATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_ALLOCATE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_MUTEX_CREATE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_MUTEX_CREATE, mutex_ptr, inherit); TX_EL_END_FILTER +#define TX_EL_MUTEX_DELETE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_DELETE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_GET, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PRIORITIZE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PRIORITIZE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PUT_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_PUT, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PERFORMANCE_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER + + +#endif + + +/* Define Event Log function prototypes. */ + +VOID _tx_el_initialize(VOID); +UINT _tx_el_thread_register(TX_THREAD *thread_ptr); +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr); +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4); +VOID _tx_el_thread_running(TX_THREAD *thread_ptr); +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr); +VOID _tx_el_interrupt(UINT interrupt_number); +VOID _tx_el_interrupt_end(UINT interrupt_number); +VOID _tx_el_interrupt_control_call(void); +VOID _tx_el_event_log_on(void); +VOID _tx_el_event_log_off(void); +VOID _tx_el_event_filter_set(UINT filter); + + +/* Define macros that are used inside the ThreadX source code. + If event logging is disabled, these macros will be defined + as white space. */ + +#ifdef TX_ENABLE_EVENT_LOGGING +#ifndef TX_NO_EVENT_INFO +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =\ + (ULONG) e;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#endif +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) +#define TX_EL_THREAD_REGISTER(a) +#define TX_EL_THREAD_UNREGISTER(a) +#define TX_EL_INITIALIZE +#endif + +#endif + diff --git a/ports/cortex_a7/ghs/inc/tx_ghs.h b/ports/cortex_a7/ghs/inc/tx_ghs.h new file mode 100644 index 00000000..ca976916 --- /dev/null +++ b/ports/cortex_a7/ghs/inc/tx_ghs.h @@ -0,0 +1,77 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#ifndef _TX_GHS_H_ +#define _TX_GHS_H_ + +#include +#include +#include +#include + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +extern void *__ghs_GetThreadLocalStorageItem(int specifier); + +/* Thread-local storage routines for Green Hills releases 5.x and beyond. + The following specifiers are used when calling + __ghs_GetThreadLocalStorageItem. + + If __ghs_GetThreadLocalStorageItem is customized to + return a per-thread errno value, define the preprocessor symbol + USE_THREAD_LOCAL_ERRNO in ind_errn.c. + */ + +enum __ghs_ThreadLocalStorage_specifier { + __ghs_TLS_asctime_buff, + __ghs_TLS_tmpnam_space, + __ghs_TLS_strtok_saved_pos, + __ghs_TLS_Errno, + __ghs_TLS_gmtime_temp, + __ghs_TLS___eh_globals, + __ghs_TLS_SignalHandlers +}; +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ +typedef void (*SignalHandler)(int); + +typedef struct +{ + int Errno; /* errno. */ + SignalHandler SignalHandlers[_SIGMAX]; /* signal() buffer. */ + char tmpnam_space[L_tmpnam]; /* tmpnam(NULL) buffer. */ + char asctime_buff[30]; /* . */ + char *strtok_saved_pos; /* strtok() position. */ + struct tm gmtime_temp; /* gmtime() and localtime() buffer. */ + void *__eh_globals; /* Pointer for C++ exception handling. */ +} ThreadLocalStorage; + +ThreadLocalStorage *GetThreadLocalStorage(void); +#endif + + +void __ghsLock(void); +void __ghsUnlock(void); + +int __ghs_SaveSignalContext(jmp_buf); +void __ghs_RestoreSignalContext(jmp_buf); + +/* prototypes for FILE lock routines. */ +void __ghs_flock_file(void *); +void __ghs_funlock_file(void *); +int __ghs_ftrylock_file(void *); +void __ghs_flock_create(void **); +void __ghs_flock_destroy(void *); + +/* prototype for GHS/ThreadX error shell checking. */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal); + +#endif /* _TX_GHS_H_ */ diff --git a/ports/cortex_a7/green/inc/tx_port.h b/ports/cortex_a7/ghs/inc/tx_port.h similarity index 91% rename from ports/cortex_a7/green/inc/tx_port.h rename to ports/cortex_a7/ghs/inc/tx_port.h index b72f562c..58b04237 100644 --- a/ports/cortex_a7/green/inc/tx_port.h +++ b/ports/cortex_a7/ghs/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-A7/Green Hills */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-A7/GHS */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -78,7 +78,7 @@ #include "tx_ghs.h" -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -114,12 +114,12 @@ typedef unsigned short USHORT; #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #ifdef TX_ENABLE_FIQ_SUPPORT #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ @@ -134,13 +134,13 @@ typedef unsigned short USHORT; /* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ -#define TX_EL_TICKS_PER_SECOND 1000000 +#define TX_EL_TICKS_PER_SECOND 1000000 /* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply - simulate the time-stamp source with a counter. */ + simulate the time-stamp source with a counter. */ -#define read_tbu() _tx_el_time_base_upper -#define read_tbl() ++_tx_el_time_base_lower +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower /* Define the port specific options for the _tx_build_options variable. This variable indicates @@ -174,7 +174,7 @@ typedef unsigned short USHORT; #define TX_INLINE_INITIALIZATION -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -186,16 +186,16 @@ typedef unsigned short USHORT; /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 #define TX_THREAD_EXTENSION_2 ULONG tx_thread_vfp_enable; \ VOID * tx_thread_eh_globals; \ int Errno; /* errno. */ \ char * strtok_saved_pos; /* strtok() position. */ -#define TX_THREAD_EXTENSION_3 +#define TX_THREAD_EXTENSION_3 /* Define the port extensions of the remaining ThreadX objects. */ @@ -209,11 +209,11 @@ typedef unsigned short USHORT; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -243,7 +243,7 @@ typedef unsigned short USHORT; extern void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr); \ __tx_cpp_exception_cleanup(thread_ptr); \ } -#else +#else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) \ { \ #pragma weak __cpp_exception_cleanup \ @@ -281,18 +281,18 @@ typedef unsigned short USHORT; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) m = m & ((ULONG) (-((LONG) m))); \ b = __CLZ32(m); \ - b = 31 - b; + b = 31 - b; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -302,7 +302,7 @@ typedef unsigned short USHORT; unsigned int _tx_thread_interrupt_disable(void); void _tx_thread_interrupt_restore(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); @@ -310,7 +310,7 @@ void _tx_thread_interrupt_restore(unsigned int new_po #else -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER >= 350) @@ -349,7 +349,7 @@ asm int disable_ints(void) MSR CPSR_c,r1 #else #ifdef TX_ENABLE_FIQ_SUPPORT - CPSID if + CPSID if #else CPSID i #endif @@ -395,7 +395,7 @@ void tx_thread_vfp_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A7/Green Hills Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A7/Green Hills Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports/cortex_a7/green/readme_threadx.txt b/ports/cortex_a7/ghs/readme_threadx.txt similarity index 100% rename from ports/cortex_a7/green/readme_threadx.txt rename to ports/cortex_a7/ghs/readme_threadx.txt diff --git a/ports/cortex_a7/ghs/src/tx_el.c b/ports/cortex_a7/ghs/src/tx_el.c new file mode 100644 index 00000000..d8f056d7 --- /dev/null +++ b/ports/cortex_a7/ghs/src/tx_el.c @@ -0,0 +1,1165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_EL_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_el.h" +#include "string.h" + + +/* Define global variables used to manage the event pool. */ + +UCHAR *_tx_el_tni_start; +UCHAR **_tx_el_current_event; +UCHAR *_tx_el_event_area_start; +UCHAR *_tx_el_event_area_end; +UINT _tx_el_maximum_events; +ULONG _tx_el_total_events; +UINT _tx_el_event_filter; +ULONG _tx_el_time_base_upper; +ULONG _tx_el_time_base_lower; + +extern char __ghsbegin_eventlog[]; +extern char __ghsend_eventlog[]; + +extern TX_THREAD *_tx_thread_current_ptr; +UINT _tx_thread_interrupt_control(UINT new_posture); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the Event Log (in the format dictated by the */ +/* GHS Event Analyzer) and sets up various information for subsequent */ +/* operation. The start and end of the Event Log is determined by the */ +/* .eventlog section in the linker control file. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_initialize(VOID) +{ + +UCHAR *work_ptr; +UCHAR *read_ptr; +ULONG event_log_size; +UCHAR *end_ptr; +UINT i; + + + /* Clear total event counter. */ + _tx_el_total_events = 0; + + /* Clear event filter. */ + _tx_el_event_filter = 0; + + /* First, pickup the starting and ending address of the Event Log memory. */ + work_ptr = (unsigned char *) __ghsbegin_eventlog; + end_ptr = (unsigned char *) __ghsend_eventlog; + + /* Calculate the event log size. */ + event_log_size = end_ptr - work_ptr; + + /* Subtract off the number of bytes in the header and the TNI area. */ + event_log_size = event_log_size - (TX_EL_HEADER_SIZE + + (TX_EL_TNI_ENTRY_SIZE * TX_EL_TNIS)); + + /* Make sure the event log is evenly divisible by the event size. */ + event_log_size = (event_log_size/TX_EL_EVENT_SIZE) * TX_EL_EVENT_SIZE; + + /* Build the Event Log header. */ + + /* Setup the Event Log Version ID. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_VERSION_ID; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the TNIS (number of thread names) field. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_TNIS; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the EVPS (event pool size) field. */ + *((ULONG *) work_ptr) = event_log_size; + work_ptr = work_ptr + sizeof(ULONG); + + /* Remember the maximum number of events. */ + _tx_el_maximum_events = event_log_size/TX_EL_EVENT_SIZE; + + /* Setup max_events field. */ + *((ULONG *) work_ptr) = _tx_el_maximum_events; + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup the evploc (location of event pool). */ + *((ULONG *) work_ptr) = (ULONG) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Save the current event pointer. */ + _tx_el_current_event = (UCHAR **) work_ptr; + + /* Setup event_ptr (pointer to oldest event) field to the start + of the event pool. */ + *_tx_el_current_event = (UCHAR *) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup tbfreq (the number of ticks in a second) field. */ + *((ULONG *) work_ptr) = TX_EL_TICKS_PER_SECOND; + work_ptr = work_ptr + sizeof(ULONG); + + /* At this point we are pointing at the Thread Name Information (TNI) array. */ + + /* Remember the start of this for future updates. */ + _tx_el_tni_start = work_ptr; + + /* Clear the entire TNI array, this is the initial setting. */ + end_ptr = work_ptr + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE); + memset((void *)work_ptr, 0, (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = end_ptr; + + /* At this point, we are pointing at the actual Event Entry area. */ + + /* Remember the start of the actual event log area. */ + _tx_el_event_area_start = work_ptr; + + /* Clear the entire Event area. */ + end_ptr = work_ptr + event_log_size; + memset((void *)work_ptr, 0, event_log_size); + work_ptr = end_ptr; + + /* Save the end pointer for later use. */ + _tx_el_event_area_end = work_ptr; + + /* Setup an entry to resolve all activities from initialization and from + an idle system. */ + work_ptr = _tx_el_tni_start; + read_ptr = (UCHAR *) "Initialization/System Idle"; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID to NULL. */ + *((ULONG *) (_tx_el_tni_start + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) TX_NULL; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (_tx_el_tni_start + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Clear the time base global variables. */ + _tx_el_time_base_upper = 0; + _tx_el_time_base_lower = 0; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_register(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT i; + + + /* First of all, search for a free slot in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is available. */ + if (*(entry_ptr + TX_EL_TNI_VALID_OFFSET) == TX_EL_INVALID_ENTRY) + break; + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Check to see if there were no more valid entries. */ + if (i >= TX_EL_TNIS) + return(TX_EL_NO_MORE_TNI_ROOM); + + /* Otherwise, we have room in the TNI and a valid record. */ + + /* Setup the thread's name. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) thread_ptr; + + /* Setup the thread priority. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_PRIORITY_OFF)) = (ULONG) thread_ptr -> tx_thread_priority; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (entry_ptr + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Thread name has been registered. */ + return(TX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT found; +UINT i, j; + + + /* First of all, search for a match in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is a match. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + found = TX_TRUE; + j = 0; + do + { + + /* Determine if this character is the same. */ + if (*work_ptr != *read_ptr) + { + + /* Set found to false and fall out of the loop. */ + found = TX_FALSE; + break; + } + else if (*work_ptr == 0) + { + + /* Null terminated, just break the loop. */ + break; + } + else + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + } + + /* Increment the character count. */ + j++; + + } while(j < TX_EL_TNIS); + + + /* Was a match found? */ + if (found) + { + + /* Yes, mark the entry as available now. */ + *(entry_ptr + TX_EL_TNI_VALID_OFFSET) = TX_EL_INVALID_ENTRY; + + /* Get out of the loop! */ + break; + } + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Determine status to return. */ + if (found) + return(TX_SUCCESS); + else + return(TX_EL_NAME_NOT_FOUND); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_user_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user event into the event log. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* sub_type Event subtype for kernel call */ +/* info_1 First information field */ +/* info_2 Second information field */ +/* info_3 Third information field */ +/* info_4 Fourth information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT upper_tb; +UCHAR *entry_ptr; + + /* Disable interrupts. */ + TX_DISABLE + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_USER_EVENT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) sub_type; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) info_1; + + /* Store the second info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) = + (ULONG) info_2; + + /* Store the third info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) = + (ULONG) info_3; + + /* Store the fourth info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) = + (ULONG) info_4; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + /* Restore interrupts. */ + TX_RESTORE +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_running PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread change event into the event */ +/* log, which indicates that a context switch is taking place. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_schedule ThreadX scheduler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_running(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) 0; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) thread_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_preempted PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread preempted event into the event */ +/* log, which indicates that an interrupt occurred that made a higher */ +/* priority thread ready for execution. In this case, the previously */ +/* executing thread has an event entered to indicate it is no longer */ +/* running. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_context_restore ThreadX context restore */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_STATUS_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_READY; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt event into the log, which */ +/* indicates the start of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_INTERRUPT_SUB_TYPE; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt end event into the log, which */ +/* indicates the end of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt_end(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_END_OF_INTERRUPT; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_control PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function remaps the tx_interrupt_control service call so that */ +/* it can be tracked in the event log. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt posture */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_interrupt_control Interrupt control service */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_interrupt_control(UINT new_posture) +{ + +TX_INTERRUPT_SAVE_AREA +UINT old_posture; + + + TX_EL_NO_INTERRUPT_EVENTS + + TX_DISABLE + TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_INTERRUPT_CONTROL, _tx_thread_current_ptr, new_posture) + TX_RESTORE + + TX_EL_END_FILTER + + old_posture = _tx_thread_interrupt_control(new_posture); + return(old_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_on PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables all event filters. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_on(void) +{ + + /* Disable all event filters. */ + _tx_el_event_filter = TX_EL_ENABLE_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_off PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets all event filters, thereby turning event */ +/* logging off. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_off(void) +{ + + /* Set all event filters. */ + _tx_el_event_filter = TX_EL_FILTER_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the events filters specified by the user. */ +/* */ +/* INPUT */ +/* */ +/* filter Events to filter */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_filter_set(UINT filter) +{ + + /* Apply the user event filter. */ + _tx_el_event_filter = filter; +} + diff --git a/ports/cortex_a7/ghs/src/tx_ghs.c b/ports/cortex_a7/ghs/src/tx_ghs.c new file mode 100644 index 00000000..30b8054e --- /dev/null +++ b/ports/cortex_a7/ghs/src/tx_ghs.c @@ -0,0 +1,485 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" +#include +#include + +/* Allow these routines to access the following ThreadX global variables. */ +extern ULONG _tx_thread_created_count; +extern TX_THREAD *_tx_thread_created_ptr; +extern TX_THREAD *_tx_thread_current_ptr; + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +/* Thread-local storage routines for Green Hills releases 5.x and above. */ +/* + Thread-Local (Per-Thread) Library Data Retrieval + ================================================ + + __ghs_ThreadLocalStorage_specifier defines all library data items + that the Green Hills libraries allow to be allocated per-thread. + + An implementation can choose which of these data items to allocate + for each thread. For example, an implementation may choose to + allocate an errno value for each thread, but not the strtok_saved_pos + pointer. The application could then use strtok_r instead of strtok for + correct operation. + + To add per-thread library data, define one of the + TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item + or items in each thread control block TX_THREAD. + + If C++ with exceptions is being used, the __eh_globals entry must be + allocated for each thread. This is typically done by default using + TX_THREAD_EXTENSION_1 in tx_port.h. + + If __ghs_GetThreadLocalStorageItem is customized to return a + per-thread errno value, you should also: + + * Customize the System Library for your project + * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in + src/libsys/ind_errn.c + + If you customize the System Library, you should remove ind_thrd.c + from the libsys.gpj subproject. + + */ + +/* Provide global __eh_globals value to support C++ exception handling + outside a thread context. This name also forces this module to be + included in the linked program instead of the ind_thrd.o module from + the System Library libsys.a. + */ +static void *__eh_globals; + +#pragma ghs startnomisra +void *__ghs_GetThreadLocalStorageItem(int specifier) +{ + void *ptlsitem = (void *)0; + switch (specifier) { + case (int)__ghs_TLS_Errno: + /* Set ptslsitem to the address of the per-thread errno value. + The per-thread errno value should have the type int. + + If returning a per-thread errno value, follow the steps + above. + + This item is used by numerous library functions. + */ + break; + case (int)__ghs_TLS_SignalHandlers: + /* Set ptslsitem to the address of the per-thread SignalHandlers + array. The per-thread SignalHandlers array should have the + array type as in the following declaration: + SignalHandler SignalHandlers[_SIGMAX]; + The SignalHandler type and _SIGMAX constant are defined in + ind_thrd.h. + + This item is used by the library functions signal() and + raise(). + */ + break; + case (int)__ghs_TLS_asctime_buff: + /* Set ptslsitem to the address of the per-thread asctime_buff + array. The per-thread asctime_buff array should have the + array type as in the following declaration: + char asctime_buff[30]; + + This item is used by the library functions asctime() and + ctime(). The library provides asctime_r() and ctime_r(), + inherently thread-safe versions of these functions. + */ + break; + case (int)__ghs_TLS_tmpnam_space: + /* Set ptslsitem to the address of the per-thread tmpnam_space + array. The per-thread tmpnam_space array should have the + array type as in the following declaration: + char tmpnam_space[L_tmpnam]; + The constant is defined in + + This item is used by the library function tmpnam() when + passed NULL. The library provides tmpnam_r(), an + inherently thread-safe version of tmpnam(). + */ + break; + case (int)__ghs_TLS_strtok_saved_pos: + /* Set ptslsitem to the address of the per-thread + strtok_saved_pos pointer. The per-thread strtok_saved_pos + pointer should have the type "char *". + + This item is used by the library function strtok(). + The library provides strtok_r(), an inherently thread-safe + version of strtok(). + */ + break; + case (int)__ghs_TLS_gmtime_temp: + /* Set ptslsitem to the address of the per-thread gmtime_temp + value. The per-thread gmtime_temp value should have the + type "struct tm" defined in time.h, included by indos.h. + + This item is used by the library functions gmtime() and + localtime(). The library provides gmtime_r() and + localtime_r(), inherently thread-safe versions of these + functions. + */ + break; + case (int)__ghs_TLS___eh_globals: + /* Set ptslsitem to the address of the per-thread __eh_globals + value. The per-thread __eh_globals value should have the + type "void *". + + This item is used by C++ exception handling. + */ + if (_tx_thread_current_ptr) + ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + ptlsitem = (void *)&__eh_globals; + break; + } + return ptlsitem; +} +#pragma ghs endnomisra +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ + +/* + * ThreadX C and C++ thread-safe library support routines. + * + * This implementation merely tries to guarantee thread safety within + * individual C library calls such as malloc() and free(), but it does + * not attempt to solve the problems associated with the following + * multithreaded issues: + * + * 1. Use of errno. This can be made thread-safe by adding errno + * to TX_THREAD_PORT_EXTENSION and using that within a modified + * version of libsys/ind_errno.c. + * + * 2. Thread safety ACROSS library calls. Certain C library calls either + * return pointers to statically-allocated data structures or maintain + * state across calls. These include strtok(), asctime(), gmtime(), + * tmpnam(NULL), signal(). To make such C library routines thread-safe + * would require adding a ThreadLocalStorage struct to the thread control + * block TX_THREAD. Since relatively few applications make use of these + * library routines, the implementation provided here uses a single, global + * ThreadLocalStorage data structure rather than greatly increasing the size + * of the thread control block TX_THREAD. + * + * The ThreadX global variable _tx_thread_current_ptr points to the + * current thread's control block TX_THREAD. If a ThreadLocalStorage struct + * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage + * should be modified to return &(_tx_thread_current_ptr->tx_tls). + */ + +static ThreadLocalStorage GlobalTLS; + +ThreadLocalStorage *GetThreadLocalStorage() +{ + return &GlobalTLS; +} +#endif + +/* + * Use a global ThreadX mutex to implement thread safety within C and C++ + * library routines. + * + */ +TX_MUTEX __ghLockMutex; + +/* + * Acquire general lock. Blocks until the lock becomes available. + * Use tx_mutex_get to implement __ghsLock + */ +void __ghsLock(void) +{ + tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER); +} + +/* + * Release general lock + * Use tx_mutex_put to implement __ghsUnlock + */ +void __ghsUnlock(void) +{ + tx_mutex_put(&__ghLockMutex); +} + +/* ThreadX Initialization function prototype. */ +void _tx_initialize_kernel_setup(void); + +void __gh_lock_init(void) +{ + /* Initialize the low-level portions of ThreadX. */ + _tx_initialize_kernel_setup(); + + /* Create the global thread lock mutex. */ + tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT); +} + +/* + Saving State Across setjmp() Calls + ================================== + + These routines can be used to save and restore arbitrary state + across calls to setjmp() and longjmp(). +*/ +int __ghs_SaveSignalContext(jmp_buf jmpbuf) +{ + return 0; +} + +/* Restore arbitrary state across a longjmp() */ +void __ghs_RestoreSignalContext(jmp_buf jmpbuf) +{ +} + +#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560) +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +#else + if (_tx_thread_current_ptr) + + /* Return thread-specific __eh_globals pointer. */ + return _tx_thread_current_ptr->tx_thread_eh_globals; + else + /* Return the global __eh_globals pointer. */ + return GlobalTLS.__eh_globals; +#endif +} +#endif + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +#pragma weak __cpp_exception_init +extern void __cpp_exception_init(void **); +#pragma weak __cpp_exception_cleanup +extern void __cpp_exception_cleanup(void **); + +/* __tx_cpp_exception_init retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_init. + */ +void __tx_cpp_exception_init(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_init) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_init(peh_globals); + } +} + +/* __tx_cpp_exception_cleanup retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_cleanup. + */ +void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_cleanup) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_cleanup(peh_globals); + } +} + +/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize + exceptions for the global context. + */ +void __ghs_cpp_exception_init() { + __tx_cpp_exception_init((void *)0); +} + +/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up + exceptions for the global context. + */ +void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + __tx_cpp_exception_cleanup((void *)0); +} +#endif + + +/* + File Locks + ====================== + + These routines can be customized to implement per-file locks to allow + thread-safe I/O. + +*/ + +/* Acquire lock for FILE *addr */ +void __ghs_flock_file(void *addr) +{ + tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER); +} + +/* Release lock for FILE *addr */ +void __ghs_funlock_file(void *addr) +{ + tx_mutex_put((TX_MUTEX *)addr); +} + +/* Non blocking acquire lock for FILE *addr. May return -1 if */ +/* not implemented. Returns 0 on success and nonzero otherwise. */ +int __ghs_ftrylock_file(void *addr) +{ + return -1; +} + +/* Calls to initialize local lock data structures before they */ +/* are used. */ +void __ghs_flock_create(void **addr) +{ + *addr = (void *)(&__ghLockMutex); +} +void __ghs_flock_destroy(void *addr) {} + + +/* + * ThreadX Peak Stack Checking support routines. + * + * All of these routines are called by MULTI's ThreadX-aware debugging + * package to determine the peak stack use for one thread or for all threads. + * + * These routines are included in this file in order to guarantee that they will + * be available while debugging with MULTI. These routines are not referenced by + * any other part of the ThreadX system. + * + * _txs_thread_stack_check: return the peak stack usage for a thread. + * + * _txs_thread_stack_check_2: store the peak stack usage for all threads + * in the tx_thread_stack_size field of each thread + * control block, TX_THREAD. This routine takes + * advantage of the redundancy within the TX_THREAD + * structure since tx_thread_stack_size can be computed + * from the tx_thread_stack_start and tx_thread_stack_end + * fields of TX_THREAD. + * + * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2 + * call by computing the stack size for each + * thread and storing the result in the + * tx_thread_stack_size field of each thread control + * block TX_THREAD. + * + * These three routines do not support architectures such as i960 or StarCore + * where the stack grows up instead of down. + * + */ +#ifndef TX_DISABLE_STACK_CHECKING + +ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr) +{ + CHAR *cp; /* Pointer inside thread's stack. */ + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start; + cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Return the number of bytes from cp up to and including the + end of the stack. */ + return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1); + } + } + return thread_ptr->tx_thread_stack_size; +} + + +int _txs_thread_stack_check_2(void) { + CHAR * cp; /* Pointer inside thread's stack. */ + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end; + ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Store the number of bytes from cp up to and including the + end of the stack in the tx_thread_stack_size field. */ + tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1; + break; + } + + } + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +int _txs_thread_stack_check_2_fixup(void) { + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and + tx_thread_stack_start fields. */ + tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1; + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +#endif /* TX_DISABLE_STACK_CHECKING */ diff --git a/ports/cortex_a7/ghs/src/tx_ghse.c b/ports/cortex_a7/ghs/src/tx_ghse.c new file mode 100644 index 00000000..6369df77 --- /dev/null +++ b/ports/cortex_a7/ghs/src/tx_ghse.c @@ -0,0 +1,49 @@ +/* + * ThreadX C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 560) +#ifdef _WIN32 +/* Windows uses a different linker, so include a stub routine, never called, + to pull in __cpp_exception_init and __cpp_exception_cleanup */ +extern void __cpp_exception_init(void **); +extern void __cpp_exception_cleanup(void **); +void __tx_win32_pull_in_exceptions(void) { + __cpp_exception_init(0); + __cpp_exception_cleanup(0); +} +#else +#pragma ghs reference __cpp_exception_init +#pragma ghs reference __cpp_exception_cleanup +#endif + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +} +#endif diff --git a/ports/cortex_a7/green/src/tx_thread_context_restore.arm b/ports/cortex_a7/ghs/src/tx_thread_context_restore.arm similarity index 94% rename from ports/cortex_a7/green/src/tx_thread_context_restore.arm rename to ports/cortex_a7/ghs/src/tx_thread_context_restore.arm index 1ab12905..56f10c75 100644 --- a/ports/cortex_a7/green/src/tx_thread_context_restore.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,47 +41,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_context_restore(VOID) @@ -117,13 +114,13 @@ _tx_thread_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -135,7 +132,7 @@ _tx_thread_context_restore: __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -225,7 +222,7 @@ _tx_skip_irq_vfp_save: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - + STR r2, [r0, 24] # Save thread's time-slice MOV r2, 0 # Clear value STR r2, [r3] # Disable global time-slice flag diff --git a/ports/cortex_a7/green/src/tx_thread_context_save.arm b/ports/cortex_a7/ghs/src/tx_thread_context_save.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_context_save.arm rename to ports/cortex_a7/ghs/src/tx_thread_context_save.arm index 6e0fa647..1cf25f2d 100644 --- a/ports/cortex_a7/green/src/tx_thread_context_save.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,46 +39,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_context_save(VOID) @@ -93,7 +90,7 @@ _tx_thread_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers #ifdef TX_ENABLE_FIQ_SUPPORT #ifdef TX_BEFORE_ARMV6 @@ -119,7 +116,7 @@ _tx_thread_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -135,7 +132,7 @@ _tx_thread_context_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing __tx_thread_not_nested_save: /* } */ @@ -149,13 +146,13 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, r10, r12, lr} # Store other registers /* Save the current stack pointer in the thread's control block. */ @@ -175,7 +172,7 @@ __tx_thread_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing /* } else @@ -185,7 +182,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit @@ -200,7 +197,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, 16 # Recover saved registers - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing .type _tx_thread_context_save,$function .size _tx_thread_context_save,.-_tx_thread_context_save diff --git a/ports/cortex_a7/green/src/tx_thread_fiq_context_restore.arm b/ports/cortex_a7/ghs/src/tx_thread_fiq_context_restore.arm similarity index 94% rename from ports/cortex_a7/green/src/tx_thread_fiq_context_restore.arm rename to ports/cortex_a7/ghs/src/tx_thread_fiq_context_restore.arm index 450769ac..47889680 100644 --- a/ports/cortex_a7/green/src/tx_thread_fiq_context_restore.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_fiq_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -42,47 +42,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_restore Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_restore Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the fiq interrupt context when processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* FIQ ISR Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the fiq interrupt context when processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* FIQ ISR Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_fiq_context_restore(VOID) @@ -114,13 +111,13 @@ _tx_thread_fiq_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_fiq_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -132,7 +129,7 @@ _tx_thread_fiq_context_restore: __tx_thread_fiq_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ diff --git a/ports/cortex_a7/green/src/tx_thread_fiq_context_save.arm b/ports/cortex_a7/ghs/src/tx_thread_fiq_context_save.arm similarity index 91% rename from ports/cortex_a7/green/src/tx_thread_fiq_context_save.arm rename to ports/cortex_a7/ghs/src/tx_thread_fiq_context_save.arm index d3ad27a4..58ea3016 100644 --- a/ports/cortex_a7/green/src/tx_thread_fiq_context_save.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_fiq_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -33,46 +33,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_save Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_save Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_fiq_context_save(VOID) @@ -87,7 +84,7 @@ _tx_thread_fiq_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state CMP r2, 0 # Is this the first interrupt? @@ -102,7 +99,7 @@ _tx_thread_fiq_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -118,7 +115,7 @@ _tx_thread_fiq_context_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing __tx_thread_fiq_not_nested_save: /* } */ @@ -132,16 +129,16 @@ __tx_thread_fiq_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, lr} # Store other registers, Note that we don't - /* # need to save sl and ip since FIQ has - # copies of these registers. Nested + /* # need to save sl and ip since FIQ has + # copies of these registers. Nested # interrupt processing does need to save # these registers. */ @@ -162,7 +159,7 @@ __tx_thread_fiq_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing /* } else @@ -182,15 +179,15 @@ __tx_thread_fiq_idle_system_save: #endif /* Not much to do here, save the current SPSR and LR for possible - use in IRQ interrupted in idle system conditions, and return to + use in IRQ interrupted in idle system conditions, and return to FIQ interrupt processing. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, lr} # Store other registers that will get used - /* # or stripped off the stack in context + /* # or stripped off the stack in context # restore */ - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing .type _tx_thread_fiq_context_save,$function .size _tx_thread_fiq_context_save,.-_tx_thread_fiq_context_save diff --git a/ports/cortex_a7/green/src/tx_thread_fiq_nesting_end.arm b/ports/cortex_a7/ghs/src/tx_thread_fiq_nesting_end.arm similarity index 91% rename from ports/cortex_a7/green/src/tx_thread_fiq_nesting_end.arm rename to ports/cortex_a7/ghs/src/tx_thread_fiq_nesting_end.arm index 4e9c058d..f2321a44 100644 --- a/ports/cortex_a7/green/src/tx_thread_fiq_nesting_end.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_fiq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,48 +41,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_end Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_end Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ -/* processing from system mode back to FIQ mode prior to the ISR */ -/* calling _tx_thread_fiq_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ +/* processing from system mode back to FIQ mode prior to the ISR */ +/* calling _tx_thread_fiq_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_fiq_nesting_start.arm b/ports/cortex_a7/ghs/src/tx_thread_fiq_nesting_start.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_fiq_nesting_start.arm rename to ports/cortex_a7/ghs/src/tx_thread_fiq_nesting_start.arm index 7ebf3e47..2922e195 100644 --- a/ports/cortex_a7/green/src/tx_thread_fiq_nesting_start.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_fiq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,45 +39,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_start Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_start Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_context_save has been called and switches the FIQ */ -/* processing to the system mode so nested FIQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_context_save has been called and switches the FIQ */ +/* processing to the system mode so nested FIQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_interrupt_control.arm b/ports/cortex_a7/ghs/src/tx_thread_interrupt_control.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_interrupt_control.arm rename to ports/cortex_a7/ghs/src/tx_thread_interrupt_control.arm index c3bf37a0..44874700 100644 --- a/ports/cortex_a7/green/src/tx_thread_interrupt_control.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_interrupt_control.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,39 +38,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_interrupt_disable.arm b/ports/cortex_a7/ghs/src/tx_thread_interrupt_disable.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_interrupt_disable.arm rename to ports/cortex_a7/ghs/src/tx_thread_interrupt_disable.arm index cecddc22..9062639e 100644 --- a/ports/cortex_a7/green/src/tx_thread_interrupt_disable.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_interrupt_disable.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,38 +38,38 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_interrupt_restore.arm b/ports/cortex_a7/ghs/src/tx_thread_interrupt_restore.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_interrupt_restore.arm rename to ports/cortex_a7/ghs/src/tx_thread_interrupt_restore.arm index 930d889e..3528dddb 100644 --- a/ports/cortex_a7/green/src/tx_thread_interrupt_restore.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_interrupt_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -31,39 +31,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_irq_nesting_end.arm b/ports/cortex_a7/ghs/src/tx_thread_irq_nesting_end.arm similarity index 91% rename from ports/cortex_a7/green/src/tx_thread_irq_nesting_end.arm rename to ports/cortex_a7/ghs/src/tx_thread_irq_nesting_end.arm index 27e85ace..cddd4676 100644 --- a/ports/cortex_a7/green/src/tx_thread_irq_nesting_end.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_irq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -40,48 +40,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_end Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_end Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ -/* processing from system mode back to IRQ mode prior to the ISR */ -/* calling _tx_thread_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ +/* processing from system mode back to IRQ mode prior to the ISR */ +/* calling _tx_thread_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_irq_nesting_start.arm b/ports/cortex_a7/ghs/src/tx_thread_irq_nesting_start.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_irq_nesting_start.arm rename to ports/cortex_a7/ghs/src/tx_thread_irq_nesting_start.arm index e9473ee3..f87ef59f 100644 --- a/ports/cortex_a7/green/src/tx_thread_irq_nesting_start.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_irq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,45 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_start Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_start Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_context_save has been called and switches the IRQ */ -/* processing to the system mode so nested IRQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_context_save has been called and switches the IRQ */ +/* processing to the system mode so nested IRQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a7/green/src/tx_thread_schedule.arm b/ports/cortex_a7/ghs/src/tx_thread_schedule.arm similarity index 94% rename from ports/cortex_a7/green/src/tx_thread_schedule.arm rename to ports/cortex_a7/ghs/src/tx_thread_schedule.arm index 1f5ad790..a7c42477 100644 --- a/ports/cortex_a7/green/src/tx_thread_schedule.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_schedule.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,48 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_schedule(VOID) @@ -114,7 +111,7 @@ __tx_thread_schedule_loop: /* } while(_tx_thread_execute_ptr == TX_NULL); */ - + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ @@ -137,7 +134,7 @@ __tx_thread_schedule_loop: MOV r0, v1 # Restore temp register #endif - LDR r1, =_tx_thread_current_ptr # Pickup address of current thread + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread STR r0, [r1] # Setup current thread pointer /* Increment the run count for this thread. */ @@ -151,7 +148,7 @@ __tx_thread_schedule_loop: /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - LDR r2, =_tx_timer_time_slice # Pickup address of time slice + LDR r2, =_tx_timer_time_slice # Pickup address of time slice /* # variable */ LDR sp, [r0, 8] # Switch stack pointers STR r3, [r2] # Setup time-slice @@ -204,7 +201,7 @@ _tx_skip_solicited_vfp_restore: .type _tx_thread_schedule,$function .size _tx_thread_schedule,.-_tx_thread_schedule - + #ifdef __VFP__ .globl tx_thread_vfp_enable tx_thread_vfp_enable: diff --git a/ports/cortex_a7/green/src/tx_thread_stack_build.arm b/ports/cortex_a7/ghs/src/tx_thread_stack_build.arm similarity index 96% rename from ports/cortex_a7/green/src/tx_thread_stack_build.arm rename to ports/cortex_a7/ghs/src/tx_thread_stack_build.arm index d9450813..eb34502b 100644 --- a/ports/cortex_a7/green/src/tx_thread_stack_build.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_stack_build.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ /* thread_ptr Pointer to thread control blk */ /* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -86,10 +86,10 @@ .globl _tx_thread_stack_build _tx_thread_stack_build: - + /* Build a fake interrupt frame. The form of the fake interrupt stack on the Cortex-A7 should look like the following after it is built: - + Stack Top: 1 Interrupt stack frame type CPSR Initial value for CPSR r0 (a1) Initial value for r0 diff --git a/ports/cortex_a7/green/src/tx_thread_system_return.arm b/ports/cortex_a7/ghs/src/tx_thread_system_return.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_system_return.arm rename to ports/cortex_a7/ghs/src/tx_thread_system_return.arm index 1b9710bb..c82a0e30 100644 --- a/ports/cortex_a7/green/src/tx_thread_system_return.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_system_return.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -37,47 +37,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_system_return(VOID) @@ -93,7 +90,7 @@ _tx_thread_system_return: LDR r4, =_tx_thread_current_ptr # Pickup address of current ptr LDR r5, [r4] # Pickup current thread pointer - + #ifdef __VFP__ LDR r1, [r5, 144] # Pickup the VFP enabled flag CMP r1, 0 # Is the VFP enabled? @@ -108,7 +105,7 @@ _tx_skip_solicited_vfp_save: MOV r0, #0 # Build a solicited stack type MRS r1, CPSR # Pickup the CPSR STMDB sp!, {r0-r1} # Save type and CPSR - + /* Lockout interrupts. */ #ifdef TX_BEFORE_ARMV6 diff --git a/ports/cortex_a7/green/src/tx_thread_vectored_context_save.arm b/ports/cortex_a7/ghs/src/tx_thread_vectored_context_save.arm similarity index 92% rename from ports/cortex_a7/green/src/tx_thread_vectored_context_save.arm rename to ports/cortex_a7/ghs/src/tx_thread_vectored_context_save.arm index 9fe4ef1c..6ee4d47f 100644 --- a/ports/cortex_a7/green/src/tx_thread_vectored_context_save.arm +++ b/ports/cortex_a7/ghs/src/tx_thread_vectored_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,46 +39,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_vectored_context_save Cortex-A7/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_vectored_context_save Cortex-A7/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_vectored_context_save(VOID) @@ -142,7 +139,7 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Note: Minimal context of interrupted thread is already saved. */ @@ -174,7 +171,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit diff --git a/ports/cortex_a7/green/src/tx_timer_interrupt.arm b/ports/cortex_a7/ghs/src/tx_timer_interrupt.arm similarity index 95% rename from ports/cortex_a7/green/src/tx_timer_interrupt.arm rename to ports/cortex_a7/ghs/src/tx_timer_interrupt.arm index 71c5357b..1f566bec 100644 --- a/ports/cortex_a7/green/src/tx_timer_interrupt.arm +++ b/ports/cortex_a7/ghs/src/tx_timer_interrupt.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ @@ -32,43 +32,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-A7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-A7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Process timer expiration */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Process timer expiration */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -95,7 +95,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR r3, =_tx_timer_time_slice # Pickup address of time-slice + LDR r3, =_tx_timer_time_slice # Pickup address of time-slice LDR r2, [r3] # Pickup time-slice CMP r2, 0 # Is it non-active? BEQ __tx_timer_no_time_slice # Yes, skip time-slice processing @@ -212,7 +212,7 @@ __tx_timer_dont_activate: /* if (_tx_timer_expired_time_slice) { */ - LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired + LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired LDR r2, [r3] # Pickup the actual flag CMP r2, 0 # See if the flag is set BEQ __tx_timer_not_ts_expiration # No, skip time-slice processing diff --git a/ports/cortex_a7/ghs/src/txr_ghs.c b/ports/cortex_a7/ghs/src/txr_ghs.c new file mode 100644 index 00000000..19572e2b --- /dev/null +++ b/ports/cortex_a7/ghs/src/txr_ghs.c @@ -0,0 +1,84 @@ +/* + * ThreadX API Runtime Error Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +/* #include "tx_ghs.h" */ +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* Customized ThreadX API runtime error support routine. */ + +void _rnerr(int num, int linenum, const char*str, void*ptr, ...); + +/* __ghs_rnerr() + This is the custom runtime error checking routine. + This implementation uses the existing __rnerr() routine. + Another implementation could use the .syscall mechanism, + provided MULTI was modified to understand that. + */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal) { + TX_INTERRUPT_SAVE_AREA + int num; + /* + Initialize the stack levels value. + + Add 3 to account for the calls to _rnerr, __rnerr, and + __ghs_rnerr. + + If the implementation changes, calls to __ghs_rnerr + will not need to be changed. + + Zero is not permitted, so substitute 3 in that case. + */ + num = (stackLevels+3) & 0xf; + if (!num) { + num = 3; + } + /* + Shift the stack levels value to bits 12..15 and + insert the stack trace display value in bit 11. + Bits 0..10 are unused. + */ + num = (num << 12) | (stackTraceDisplay ? 0x800 : 0); + + /* This will mask all interrupts in the RTEC code, which is probably + unacceptable for many targets. */ + TX_DISABLE + _rnerr(num, -1, (const char *)hexVal, (void *)errMsg); + TX_RESTORE +} + + +/* ThreadX thread stack checking runtime support routine. */ + +extern char __ghsbegin_stack[]; +extern TX_THREAD *_tx_thread_current_ptr; + +void __stkchk(void) { + int i; + if(_tx_thread_current_ptr) + { + if((unsigned)(&i) <= + (unsigned)(_tx_thread_current_ptr -> tx_thread_stack_start)) + { + _rnerr(21, -1, 0, 0); + } + } + else + { + if((unsigned)(&i) <= (unsigned)__ghsbegin_stack) + { + _rnerr(21, -1, 0, 0); + } + } +} diff --git a/ports/cortex_a7/gnu/example_build/libc.a b/ports/cortex_a7/gnu/example_build/libc.a deleted file mode 100644 index 5b04fa4ed9b6479f70979f5577dd0705d1f6d1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2447586 zcmeFa3t&{$oj-o=otey#NkVu8!h0a0fFZP#VBwboTv`?Rff0~qVNZcD9w+5XD^^Er<@XNH6fh_vMj|JkFn6Ac zQJXJ>`&A*Pz9vNUCL!ie5u&6Hj|gYxLgCDNOE|0Zgj09CvOSjy zhxX2=g!7G4g!2;O?7dYuZ(c2&-))iB9bPA-tvFw}v`ZHXclEu(jh!Og^%cV133&7G zgnQ>n!u0IyeRnL4Wi(`{i5I(t3|=D?vu8#;8jsLYMm&Y z^sFeHUajoht)lSc{i5*n22ptSOQP^R*uros_mLPZZ5uD~gufDT>zoNZHgpQS^z&MbT$& z_3Y5ej|pjKJT8Ww@w^y%&O9;ng13}i|B4t&+p|U4PyIy<&BcCgg&0cveem8tQ4|lm zR}{}`7sbn8QMP@QD5kyoM~AlGdQBAXog;?1H;G{dkBec`P7%YZo=|qx@nTqPtg@HB zA%=bOK{4zL^Te>P06zDM81_@Ry$1N}XT-2S-zkQNU`I|7!zV4ww2SA6;jtenyLG7; zPWze1j;{Uj`C>TjZ=Mh%hHeofX54$QJ!^v)(K1Sm=(%3m&&?AfzR)N}Jc&58dn%Ou zOG=D@9XW2Evh$x1BUkSbBa^V#ydy^5SEuY#uZWR*5$-ReM9HKpM9I{5M9HbIDSJ_! zC~3Z5lx+WtDB1OdD0w>2-)}>}}7BQrfRA zRQAXDqIBO^5h%p7ni!r0-DSN_jF@|>4US(6~i!rpHa>ba>uT=KmCyFtz zJf3L}JR!yw%oAfry(7j>eoKs@N`Kt-Hn8|F=Sn3!N{`(w&J z@KZ7FYo~~D|8cPx_bkHvXs>6-kNlAsA5AIyk$f?J-S@=!O$(L1?R+u*;p4^l@2^ny zH=zI0C?C%?o@W;6Jp{mjmm!Y6)}|pjTMpO9#eKvyNIBVk@nIR zB0_t|n}ywU2ll82Tm7dU#k#h|M8e8d$vxL{rE$oY|rDO?9Jn)EiX7ll#hm;^0+9Ub%iKj z)-KA|-Yv>Iw}|pj+$qX;{Z84(;ZFPCmx}T?P7>vBKQGD;Tq>sKSBR-2_ll{LR*I<~ zek?w(s#U}qy4%_|$KrJzsV)((>lE?&`b1}^h{fU!7k77d5lO`BT4M38#HN-s*_LRl zZ%tKs-ipp57i7@M&6&ZIo3p$#g12>SNMwYkgB^i%MvTrxS4ITTc6g8rL_F2B1tn|n zkXUCTsflDV(XKt}JG$FZ9SmHbN}%XAo{rLxXiXTu#HOxPyk19YY41$1KFOdRJ_<`E zlCAN2&8xF3)zaS7(zsc~JL|hz+Tg5UEG1&`&Xy#+yBa}wIGrt5g|dX5GO<`Jm1t_| z>`J6!d{W6&N4?MwE#B49*a)IS{n2Kpg z#oHUI7J#G1(Ac{=;xYvyqOpEM7YZX18pfJ{AP|4)Cx4W;4AUrN0RF(WeM3vCqa6tX zi>dNRw7Xpra8)f$B8m2{)Mm!n5^F-Ult^15)s#?ZDj{39bY{aylC3F6!YhZy=$T5j zquvvnkayN#tg$1to^WHlr3+BKtBcheo7j|`9YqjX-F#3fl#Epm+&Ft~bZGBlm{?;& z2VM#KNJKOy(103|bO)RoTe~}(0Xjt^N`z-kknHGWaD_<-@0QjC^BU`HZ0kT1ZSCf3 z+}W6vl@^QFU((%@%5X~IwTgGbiS4DmJITCglrdY{QAF0U9XIXh#a1CzTKM%6WT8o^ z2Nh!_H0sw4NN0V#y%D8R2$KfBiRr~#6S1aLM|TqMZH$edrD%;ep|fl>6-6i-^^w-wF*4hg3rGF!_*BhUrWs_=p<`5tX3B(5PQHnEF>3 zox0*|4Y9_qgnI0Rnv`)#t7o%eW2yyxS*&qmyp`+;(4(N?ibn0l=iD4mfuY6(8?$s$ zb(v^L`|0XUG|0g6C+!qtMD(65ZAb}S2^u~_5Ez=-R!+o-wzjkl>sF<_?zUf8ZJE$xjR^xr6&+GL-ifXqT&OH)jD?6DXL z-TkwHv^UTt8E;`;CD|J9?20Ahc*9lTRHCap#qNSJ*C#eN#Jl2}>P)uEkrnB(bEPY~ zShi@5%FdJhdwpvh4M&A(NHoT~Tf1V-alD5yG|;9w0Hk8F1?f&%d!r9PZy4)}*R?W7 z$hyuf6V=&lscY$Ki?i=RlQoXAiFRygM0i;i44$z}GQsyrmO;g7){fU*v^3#Cv~;$1Y)qs8 zn^N&)GoXCQ3A@`d?6-7wCONjZbaq3IU^u)$Dn%eF$&DthjfVF|Inpt(NoAv9ztN<( z(Im9dq_oi_wb4s!lZmg9*u~zUp)nC_oma)dk3h6i)RTRhY~>!t$Ix2e(cUD-O^u0G zHL@n-DR#W^2HhJXXhVH-f+KmW8CmhfAyhz&QUYabJcU;rZ!UYTR+Xye)&?;XO{z1K zBVW{5eRnE_jwfaXVegAa-PX~b=!&N{A1YEhh>nEEkVv)EA0kdVgpSf$uOtrigbZxO12YU_-X_vM-<{v#h8Y}uF?azK89W=$xO4F$A4X7M1sCq03 zIwOep7&QhbJr3ZVYm2W>urq-04N<5Gy^F#49L-?UokU-gkd*A+JPd))L|$gtpq!)`Fob26rhbnCe0s!s})zEo*{KVsMCIXxxlJl-iqez*v((D`KI1%p$f> zJ@z(?B26kB3*OC!gqHtGrhl*C1> zyA?#;958HP%v8Lq1tNyZ5JoAAqHSKY)|hUHvF=U|P>|dp*L#IbyYT^-Mrk(#6&(Y5 ztj%i{5;1%!aEbQT7&`kV)uh|KEND!0v`Y;}$l((y#n6vK)CHyxI2fHw3wRtSwM3*b zgwalxM3n6$=pcX65)quF^rN6m9bzNXru2xPjbOb&y`L#@Nhj5Ht-Gh&|w4g!6j8hkxbH`ZBS-zunkcFJB2J)TnJ6vDm| z-5xX%jS;Z+x}kQfYE%#jqlP^k$i--=tr*@2K~kh-2MTlpp-B>I4#lqoU<#6xAu&*+ z@iYRoQ%Pt=8Q|B}(EvB;B15w{t#wo)q*B5npuOJssUa+P;*UnaRhCp0RFzW}lcfZU z_OxK-;dJ)i4}-!n?PgvY**v9up)p-yqSLekuOS%yg>6m_8l74diDAxzS`bMwLUIcw`wjN zYUsRCMs2t909=M_*U7i*80{uOB8q#v=90v4DbU?-#w>{%Ho+SD4#ou_rZ}9i0;#hP zOlH5UgKYs65CzbxNy$o*i1DW=CcXF$YXnXj^*mLWZZ8lpFH|>C8MRy1X?IeRn;IeQ zLGY~;Y=te5Nie+wk*%c*-6%w+hSmfImd=gP_xUu{FGwlUXvs$U>!+*%LMV+!{en~v zUCkeH7KEA-UkvkFfC=kTDZnflo`sY`*&k3EMdGU0#B0O~?r3bFDnYaptv=q>1*&s1 zhi&GQ-5MvY(9+bD3nox7Ng_Dw0)?FH)%!@0<|nfxlT#&>`In`Z*;Cn4sjxm(aw-ox zo1D%Osv4*-bG8u>H4n84vH~O?48@|08YgHZ?#a$%qa{p+R8(4Yr-LpppQ@d0(p_yy z&f5Uhm~=ulNKeQz3Z*OqjDaNRP+ApaM5VnL2nVIA7zhXGJ{Y1n#CbR|6H)6n(0aS@ z{MJL|)H)_)8Wbqo6|yiMf0x!YyVN8Ggeg`5UjblRSnAq>-d+`b3seP+qVdfTo}1~m z1*KrMFbGSEILKwI6eu+gN6Z>A*OLLW;f7nGiK~HZoh&R)@!OUOL)!@u12(K@-@w5-a>uz_0p0|=rOyUe_vG7pO z9Sl_b4W8O(Pxut4j%u0U`+AO8^Wp1Q=onIK&WQh#_Df4JZc<36(pW z6G%BiNT_1V5rVY#m%{;Q+3_P6R8*jv$b4v=Y;+=MYoufl=2>AX@Wz&gu4edlb=1k< zHi+e_-nAzX z*|kqZU+fcN)jp^A^5ITo<=%G=95`QybM_1s;&vx;N#FTm<-Q^zo;elpgy_P%^Mts+ z61WrHDqZ#aIZ7L5q|kcwL_d`02#DPZaY8d9i#s0fFbj1U5QS_twgoR1dx zicSiDIDA~ek`cq4;Z8MnyafY@Ovfc7T#%tEFi4X_Kw7@yC#VruRgTo^1SIIWhyH6gAiIj2kFoDi0?oHNMCSs7{(&Y9CBiVBB| zB32E#gv63%{JlVk&jGrEd3BvX6%`6J78a7}R(94y%dKy&Okzg1a!xdQ!mM-Vo?3}F zx3WF45z~{iW>?Ivf{L!mO3qkTc6K!Y3L#rMN@7&1m$ksW@wfZ|D`i-J-%bq_NWXh07{@-w{jCa{b7T5CzE30T@R(TZ;+`B@HKO zSRnmwIL zAT+K-Lxh^S;wPDU?o1|wP^HRnO2^G`R@HUJX{VhOnTDKAKdg+MIsx@47t@7dxgkeXbZ&0L^gJM>bqSjdhNbr|Q6X0hD(bK}U(o=qwF!&%b*aP#QGsgF zE^=zaqN(57{*6#IGaQoLbrgBPGR-88(E#(ZCKV1W74;o$ZCnbfK%Ofg9j99AFk#Wj zmXV_whj4sf_(IcE!P*VXG`q=gXE^kO(mbu90raCe4mJckSh$%8W8wf6;}n??uK6naFYk1;#KhDGcv~bt%rlnZ=IDt z3@IMJZNN<&@;H!{KjU{9{A_;QS8eiF2tSj*n}9P;6qaTZ9(33+0Zo*a-LmB@jt7GU za5KjE-3te>NE-J#gu_;A&zNw-;9}YoJ$n!i+p;}l!V#i~p6TI3gX2hmp|C6m%f+&0 z@Ph;0jWPTTZJ_P$7r#DW+zEXdB_?g>gtVO^Y33$vd_oB`#>I!0#_Xq!ojT*&PmDC5 zeA@T~pIqAbWDVG~U1G)`=5b&jFr4Ml!$Emm4N7X@S7Vxs7l9mH6qEH!h`rsy#em07 zm2irNQ4Qy7$oHE5i#1%X;VKQ~fDZpwO?PVeaSeA6BE73MyivP9q2YgN*rfes{!m>q ze~2f`fgb}+IphMFC9Do6tInP`M;%Rw3@)qQfOKIzS+y9?hfnli*)T1@eYAiAo+Tj) zS%ZpEMsvW9aN+QQ%T%1;T+#vA;3U@7FXl zK1<@J7h^cP2QzrK3}E;BWNP@$v~apJ9FFC9#)Qk30U8m4ZOa%FE?WjDK!K_e2-=qH zS(!ifr%Pc?9H4UiPY}_@_^}T%IK8OMl<6=8#&0cfyC+_bbeRs*B{bZ*@K{>l z>1}+T!&p;lu&5X1TvW9-?82Y#l8bu55C(mCfN>H@%5f5K3c~?b5~8wYeEeWTh&38T z2A?~@!MTVugO5jVs)y)}N8a3tsQnktWgI*%^*?7J=P2a-gFQzPz#N4&DIr^&YF&QW z5Lh!blVP1J@#Aj7PkI)jlvIFq!dVFA=TbbtEJQE;f|46%A%3PDp9a9xLmVI!^gG;2 z<4kv^fuvz_&V>O?Domr3QzJwNpey9G#nB!PJN00Q19M13_@+C{Agwr#%*H@-0nDPD zj$sr*NxL1E<)GZ!IM!uDRrk}5-qh;hP3_5$PueQPvHQ{DjINhsZR}j(0gv%c5IZp9SFvf(- z9_tsOJHG*eM!=%m^~PMLVd4N45fJKqB9-=A3fvwqegJ;>A5`DJ1^gliXN;*o$__Ta zm%)re}~fdp9h18VFqaUW$XKITlq6%J@c2X z@Bb1E-pb-fp4s|-FZ#XyWR`mo)|OdHK}^izM>1PxnTK$>==)U|YgtI@dz4#P&;7|P zRVZ8${ERX6VrT_k?*l%TM>HhvmsnILa!Dt7rXB z^$Tb1b*gvmDa5+dUk)7jU%4I??d7`BSbyYmea?{KSc}4%6lfPmR*9o%`8|T}y*|(* zd{|Gae&M>k#nu1z`r*i8qr9ab^n+$GDNijX1xGl}2rfEtsR-Rqp%-=mn!yk(oQ&X_ zQ~qQuJyjw=xc|kcf=t1#Fmo-cD03}JOhC!-52M7sbrM&dpR)-K+%4sgmhb{@1N@yl z_m2c2_Z<3p3sao@h1f@Z3nC$hfKbrYj$Z=h5BmQbz}GenzwSp7+6no82H?BuBlP|W zJ-;}a=ru$iWH|r75q(Go;4G5wD-vyBw5OIa0Icx+CtU;n@8dUbGk${KM}TvHoa~Mb ztq1OoBTu(XgocqC-yMtHm|b6IKVd-R*3{KkjwqTfvA7YWUxAC9Htx2!yL zIbntJn--cvze+{T41I=vQAI^VLHf;RiQO_>daw@&z4B}E<6!>xNx~7%A^;jbW9Pzx z#&+D5@f`7*!W5!7!x_L?T?D~=8MNE5a~l1r?P`+YizN&h)2N=91UGOR8w z0)m#|*YRXpz44l5bTBK zPBN}cdaPt&<-%%)6AP@$)PO}kb{LkH~T0jTJL z#{?qUnEX+XviZGamy3S%I>J3>B3b;%*J>VOWEk}f#}c?7 zNqq7-P_YMJ|71%|8_wCXZKREzI_oXd*C$8bbZB3O#>en% zacRT#PuuvQ5#g~*!@h$05!J&wgU$t`zS*wrHPh*u zj%t{_cDh`8ISII0gF#MYN|6dCR{;d}^;x-0(VH-8aq}x^*Ag310P8 z`)b9FukVb0YIv!*xztxPwb)<#Aq2l*A9#$-Zws(gK5VjV1?ROVqN;y5&{;U(bDwGPbZ1>`=>kxIiZ(P<&aWHZW21j_zsFIyl= zz4CwRhoU`cjWBLU1I*I$H1aC)tU*uF!!B^qqn@kT8 z+J>L2VTTYwKd)l>%fZMA<#Qr0!(SmaWwoUI!sn1IqlW0a6oaa7 z>NKDQe%~k%?ueg&@V_JTQbEYiZmU}#1z_KW|9;)zU#W8#{y)+dH-@_o9 zDhQu#QN=~EP0*PsS-E8+#ZZ4Q7=+kqJr6{d2<@jAG*#_#cXs%DOkvJs{-PYOF`c=J z@`v*g#;sCRDE!aN{0WLG2>+0kKTlD`;YUf$SJcSxzmv-XMU{pZk~&dQ})+bCQ)=i`=z!qsGXzidKf1>vQvjxQ;ys3=y~DBK6`l!+II zzrbX_teBLBk7K-tSvPLk`0zyL;gLm*T^0#{g)zR?A*u54*Xj55Pf2Q8_M+6xmTm;e z5hPLH9|7)v3BjDuQ2zuF!y7UKlQe-Z20oH8hWc5p!&w8)b4XJ0m6+z+O@G!&I$gwK zqYkl*UcZ4B`JXloiHc?UEOI3L?sFwCvIO&8az%3~xd>t1liiQY;`&Q!Y%)}Gzhn6p zlyoreDLQU($?@dzQTG<8GI-}pW-#ul+BaIVnSmF&EH381N?v0{F4DdWN=XgqVEzp`{9Rm+HWaN*-cD%e3#ACBJ9j zFLh5s{rJjy10^F#U*>Y9%UAYh0FQvR9azQU_?(x(W>h!Au7hKN8+1x!*BWx@#_Ar; z7KjiLTG?y3!atG0gQI@~8RiAI*)3sWPYx$F@MQN7Rm_m$n_ zl>95%HE8+)r(_HL6PkY5DM`}5QPbaaN+@QzO`86egTWOkv1(+sjS>DE5Fiu`F$116 zHkTf6)1$@cm7w>#m$)U*Gs7J&X;$lZnSRo(lO%4-CBlwNvY#MAY?6Egnl9ygv>#1% z4Bu5erthN?$6UukU*a-N5xNJzUQSBISiU{Z2)^EEH1r(m=O8|ot=BPq%=6OyoA?dA ziXXNu(=Pr85uH4LJ>rD?{$C?+e!u?-fRKM&Ed(Ivw+t}S`Cr7Nc^%fdaJ)QxQov-L zmE)nl38z4YK4-#eaB>PJGuQ#I0X&zf2 zL^-m_MSBW+01mh`30H=+R@Sw&S5{Q;@JnS=eLbhmXSL%5<}BVXSSgQqRBCRPiX^AQ z5&r zJM2aUINgSG(Y&ys3O5=i5F%NS=T!>8n%Z0czwFrvZ6pP_x7#VVuRQw~p9kPuqJn1bKufDfLw_ zh}Vp>J3Jc*M}6%E?rvfw)OU2OZ$VPAPI)1P%qH2!^i69moxBMUS!w7LyncbFvw2Vm z0(e75x4hg6*YnBLureTzBBrl<(HTdnaKzFSLlw7?x2Evm5KoIY;M6XzN5REUOc_@z zw8f>LykwzWT|9y;cC}?r;>S=y%{W?VPN#3^;z>?fgv?4#@iD6lz}j0nu>9T6RwS7q z?v0Q|F;_#97~mz#>9AR)c_J0Kuuq?`lDQVRrV=-dsKe6gb{Bp4o==$<5~y;RI_9O8 zcy{!}`F>s+sSe@Ca9vgd5C3Dqq63pt@`gKH0|1P->hK;Spv%ubZCPyP;?vfwT(xBF z;@E2DCEk#(Ou0T1vuZ{@2bmLjOdqHF*_>jhFIm2rO+`U<1CV~k=ty{n2KGyu+l){) zb%&I9l_0JyVlzVGJlcc{O1hXiG(lee8RH>sUR9_snm|c6%ETLa$szpJbto#UW~oP2 zNDCVgt}J1t;@}n<6z^fc-7~C)#&+3(p~`sWPHfF`9DkGN@8u;;vf-H=$ z;-ERsljDL1-s^(v<#-hqT4@JP;Nj9VnaM8HI-Vb|_+s7YmW-?B8gYc3Zx6ak=DJeGB}G zHNSj|AE%gXew)CL&(avj}oF9j$9LB zeKV_sEURgwK*h;uFCQVIjp1i#18w(Xu_nZ4nKqbk0`auxVCRPAVM|Jzqp?PWYW584 z&2EdNXKLTq0l9|6iC1Ijj}Bkm>W$&QJT6ZY=-a&;)D4g7s_zEgDPw{HftZC$+hDfB zjvt}+zjD3l4o%-jh$St)+k>t@p^eJ*C&1|p4_Kw)hc#qBLwCOSgcoYqtl@SIuhH;! z4Ve#ymwTW9pV0JwY4}qO`!xJN4MTWzj5kWdat+Vduuj9IhL>u1t%kR2c%O!kXb1&a z^8KEMavcfjaBnNq`?H2&ydV+3S}N6`f39iH zw@dySa*r$NMH;TuuvWu%4Yz9eu!c`)_=<*aYWR+Za@H4oN8`mN|5G%4LBpSD__~I_ z*Dwz&6AWLXVY!BLHC(9SIU3e#*rDO28eXU29U6W~!^brIu7)pZ__~IF&@hAs%zTt; zSgGMhH9S+pb2V(#aEFGU((q0VzpUYN8vb0vH#Pi|hWVT}Lb_u#oTlM?4Nuo_jfM>x zc51jy!<#kyM-9KK;gcGEU&DPG`a>#R?onp?r)juV!<`!TX~@0c4EGxi-_bA+9XQ>G z5u(kNXgE*1pQfRFui(#do9AuN&CBqp2Em&?3}jzLKB_xXx&_xt2U{ndGcWf&wJ)RX z!DOtH`g>>f;Xao=e(tNqx?};?D!Df4$NpG<@8@1Uoc*zC^)E2c${L!ySqEfIWZ$!r z-VnQF#K{wVs zu)kILx&HzR+gSZ>7~N9((38AbCFK4FzBu%>n^A%R0_Ro!gq&#rG)&aFFpsxsJHcfn zE(ne$2tLc-n!E<%zB1%+aCC++s)NRnFGGYG2`g{A2{yyJqMpA;XSXKySjwH6nXO74 zCDt=WV;U=$p&epo2-$$O=Z4s3jbTX_75UFB3SI9^29Bl*^L^MPEJjZIiy8EikAR32 zc_bpMOJVkrs*DdENZsuS^?g;^N;@kU_0!WnRH?n$m1A*YE2~=inevdj^ug05AE=#2 zsCG*EkZqa$Dh=fuf;2gu6_xl)g|9T30JMr^xn{Z_3%!$VByBQt^phH#A>EzUQPDYbfXV1fCKA6Gx^HFz#IY>@*uvd<9 zswm#IFDDt*?14*fdy{tnVztcP6_;@j;6S$kX3DPO9#)gerEHrKiDMcE(0K+wX5Uu@ zaBTmI0E7a*n@I%?Xa_~<)Ay)y9tz+Fd|#E$zFnk(zO4X|KDl4m{oy?PQaDAtpqDN# z{bMU}@}vM-6T-=$JU&hbeg1QRuH5W?ltsz`M6@GW_snQY`Y4cI+LTqSmNrY9pforR zn(BZuY6N}<%c%Fk_2aPSbyaN^4*koh5$U6TvZ7c=U2t#)OA7;tmb|U_2o^HE1+%nJ zc(7#X1)?yUyFfX>;~H!QEkmN#FFSgeBfZ{-lVI{`W7`2x`+6VDM;+oe!1u>Vnd~(Y z%BB%mV^p_y@Hv@pVvMHj#lD$uJ;RZvd_)>@gfy!)G(h$cAhKoCfx>ajF>%O);V5qz zWBeAv!Qk|J5|(e7F~%=jHvJaL!Eh|cp=8tbVDO@0259(k%(G>+R>*3mUI8kw0CJ}+;^klO`cWU@4?f#Ue z`A#36T$|jSn_OG9AXm9|KN~uldzA$0oC@b(DbpTZdDcGKkeft1#GJ~={}6NcO<2?> zjGsM+oVF&E^6f4lM!ua39P%x@!-3`70C+&`4KN;VO$6nHe4Nop%ePYYRq`#RVR?17 zmT^l-Ux@j803+i53*u-v;$o^(eOA5iXP1wrn`QFnW8 zb~jwJo0aWP z?zNkn>J|@{dtb)*Z{$1nLJUVbTkc(M+O3AN#{!-$_YM?}W0HwO77TY85p9eg$5n&V z>px+46Vb-_Wy`&$!j*l(HqWy`&kQzP&<#^ik)98hJRdBDowq2%80f?qCjZ!d(*1s?Zw zo!t*NcqNTjDKegm+&d59a*=y$@vw4{dwIY55|ar)Q!kd2L~Zx?v)s$R!j^jtZRB1< zFSIHsOYW6DeU{vt+?*-*Vob{%;-uWm58J@-J^NdddoM?RDEICpM7zIM!<)7H5zD>& zrVRw&LH8ZZpO^F9+<#yPfNtRLSnjj!&0^fZj`%aOC$B!J9vt(cSttR>nCj44`fQ-|>Fnp9mP+bLoMOpd-@%KId5X_Ov zzpq0M;J8D%ksX7PqyFCH-~A}ZeYzZj$-m!2IAdgI2qJsl_clwuWc`?WsRM4}kVmbX zoBX@Wk}rXJ{IccWo#4kYz!;NO2OLmD&pc%1?@;pZgWwnOVx{w*E&o1hL(%z+^asNe*^6w;6Y%cOI&zj~U|2E@cKNj*YwtHK~ zPMvxKTTa>>jlJ)D?`e#DYjiI|pP;-w|I;;1kSYJ_VJ1@mrelT#EQh z2L2A};fVG$V6Hp2x!<`te@n5KofNv3Y#Eq$1abb?#lBmgz6)s3y|?e(zJ`Bhm8qP{ z_FXPcX z-lg+*x5a@iYe0#2OcOLZ=ioEirXMgBbMPJqR&AENtJiD#6?e^3CBy#3TdNl!qrXA< z^e747>VX`*&*Fo*-lI3yYd1I54IVt#`)rP~?mD(v%a~C(d#-n&a8blHama?@D8CtF z{1(E&;Pm5I!ne#A1UEVs9%UthPOO`y8th*e&_B4|@KvT~b0k>sS?n$6* zz&?c%3&W9bC#)$GP|*c`M&2N8{IX?T&K)y8`5j8u{Ri-iAdE4FU$(4!hn2rW$+|rE zmCIc33pvWVJja!btotE^%SG0$L8as(>-ON`EHRY=XzImslBn%|EM#3NKYE$4Q-5z{ z-4QyiazJ~o*U)CJ*U$^Syk*O}sz=Whb>WsF>2gfV9OCRf4s6SROLM(PeUC$`X$$XS zpo~j1*j(@2Xzt$(=Q%={3^{lIsknc$cI94CgL^l7cJ1N!8}h`iJtBEyUjXti+FCyM zKZxR6`kb1cy@BG(dqwf~KHO>7C*5}=EbiV6ik@D1_htm=)Od&I?wx&TnJO-5e%E3D zsRMn}!QS#~ee=+^zwKxgChvU4fro+81{$`B5VYa zCtaAN8}$DO4)}0a9RNN6fj(CFet0T=gMXyUmHZjzOa`D~2AvB-2M7vffOVk?axRok ztB3H@h)yB5;5cjZsTDiJnR#bT{yPLCZshN|_@YAtkbs}0n8J*RKLI;RGt)S9x8(?L zhA~$z4Io1v(I#f|PD$xrUX*f(YnqCkIWuORGacVMubfr6{K|$|XH{0JQ+OOh?YVBY z4`cXT78R9V@9cE1^6l_n8Q7j@-OqXwQ!f3L25%B~9^?0Qdeh%8?!CQj5 zI%NC-YG;r=!%QBB4s{QMPmWpNG#EgQFrfvits{kf)yHBFiM*PIDIdlzYMD`9aP)z8 zaAewny1xMpRqlgeBdmfo9U^Jjp8<}8wdK~=(Px#yZ6qw?GaThk%BZ9v=1Fs(h6d1& zCR;xkC|tIFz;ImrGsf_v{9$nV%>|)OW~h+HFIzw0wXrWC2+P5t(v)K*a1#fp;7yYs zvK$P$0)BSAya0Zz2l68{{A5oL+L&5Y5X)waspo9{;3e?Gcr5vGm@xdZ^@AJ0kLw!7 znEYky2fSF@X_^n(i!E*Je^CkmB|e!#i^3X=&df0mO(O}&B@!Z}Y!!@h$00s8`D*k0|nNcuSA2}s%-7S%m4-jD-Ph8;RCV{65AVBF}e zHhh#L3eWGIOb8LDmT^7cPeK^XP(_Nb568*X7sEXmC+ezHC5_Qs2#MwhDw7me0Gb9MpEb4;k(kmJ#c1G zgng}QfK=-40A;#U=19gQ3*AN~%^%ZFiKE-)?WYjF)k(_3m-y4VM8=WGw6i=QnXP=# z(Uam2j+o)n&(0W6X`q563+u&@hc?sd)KO5L^y8I`jb>!(Eyh7hR^SJ!$dRfd{oWN~ zH&V)$G}*eynUFzq(M7(gqIN#5%sRqAd1R_6<1cqO>F7h<^l^JG$Car8;q{hl0P>M$*Pk{k_pe%v!}xBuZT*l1^N&`sV%d8qSn5}8oyCV&Naq=&VSN9ria zWTIUyaSXn`QI+#v_TqkYl-@AOnA4Sl{Obd_5Dy=sJ^ z4#MwNo9~DPBW6A0>OK@TV?Bd=fqEDH&97-RkHH`~~@CtCVrV*0sD1}nBI5+>djHA{xvh)nKrtuf}zmN3{^&aJXE4v)QQoZWUOo0Ez-!i*}B9F5Ns(EP{w6^BjZlhG~_;M?$FQxhNa2YB?bydnZd+i zJci3fmpBfQa#`2tMLAvoPnP3Qy2LEJ*tx80JOo)Yr1=q=ddb!$R-=MgHe*aZXX_G= zf}g49L+KLR!EcIT259OmTbFp+${+hTlfP_T;$HBJ^uuo(+-zNf>o_KVhtegU0>6_? zroe+J?OhrICOe>S3r|L%SocP z`?1g^%sPY0hn@O+qf1~(K^pcI)Ftd^!S?VW!~&~9F^n4$Z2W!AWcEYJ%Q8} zY7jN&`|8o4Cm=j!aJ~zKRT}=StzYos=EK$v|mtefTPbGMw zV)w3nj<{e?0c3o5AMZwA_=OX>VlTfj?CieQb;Qa&e>`yDBz_b4aPHc+cQYOG#_b}0 zU3bX{dE<8G$7@5G`;_b(VB`(k+%x2=63O=W*{$#+^h{1m@Eu(Iz^AAh2I ze`2+H>hwQK!n7%vtTmYhN}hv>xanIFGbGo;m$EaWr&FaoEz__nMO95&n~V4rYpX>aVX>DHF^giSZ5;%ylY$?m## zIDsW^VbpB#g{&qzDU$mEE9yJi+W1kiBbA`lJUg0kh74^tKiN~je=;n?K@^gvK|=%R zM{^u3Wyit7%|sXz2dE&wYlvuL{7wOFaC-d;mT!_V#_uFJ*s|RnAa=uz^M)KESPtfE z1+0k!RPalnKO~}!@mmVo=Epm#8K3-uu!bLHUK0nX*oEFZLPQ&eqd6be=GOpz#c(4( zx*L9!d2N2X!EZ9$j4^)e;b8OQJ&GoO5NkbtlzB}Y^1uSVh!D}n_+19t=EpM-CVz$S zGx_6+HRG5z;^09~H%zo2$lZYYiI*)_rYajJ10!fPLVWolQurt zQf)dYsF$RTojT*&PmDC5eA@T~pIqAbWDVFfYa(rYVi^9E;q#?zYzZQr&;{Hda&G+v!|{wjXxOd4@PoJ0vvY4C zqS6RdZ|@C?b$i}EaNsfYe#qZ-d-pFs8gu6Z9R$e!hE0}4ohfhQW<@Dik3~yXFQeMO zvs+fP>dMvr9r-o!jJflR`8)#t8UW0lZzUBp;H#i8cm7G`C>xYH5AdJL>$3pZv~wZ= zCd}n9ktcJYHtpnbg2#D*Vbg~Ic9Xm?_%cDE46ruH4V6yeF#et&WQ%tS6_L%$3V#RnCH)(==<&lFDe)8MBuiU!#u? z9Haw8Sf|?T23%A${(9$Xcc80b-Lg^#9;2!4b~5_kWha zZk0x&$)0oXf@l&!!{u0u1}`Naie^(aZ6u?&LEDC5X=cDO{9xfIQuF` z5VXPRbr}f0iN+Z86gb#Y)-5QX~9xZZ7+Gs?lENda=^=Y&l8Pq(fqG zy~4Z&ip$`7gc2@QXx2+8OB1kDr$1Xx+8m9u1ZK{CH`w+!=gxk>7`ES;h5#gO50ADY zqQ(n-qM$AFK*7m~#(DT!LOkf5n%+f-46#;@M1SFXNPXf+=jBs6KIcWrr$QjTu`c;_Ib{(}e`;i%44{|mi?{%QcIR0#kq zgy7elgII;6oh@`u??GY)u?LB*Q13MQm^$rHIFnB2gjgb{kWq2IA#}A2cOKo==1WCN zX;rK9p(Ry zUVhe$ImgegT)u7D=g(TEv@&#$8S66Tpp9XhSX5Mgy>pFwweOSuoq?kWy-up9^N?b#twWUq8celk}OdRqdzpoL|#`rA+ZE$-13IyL?V+@+D%lR>|{{ewm4$6WoFY~n$*2DoS zK8B3uqRagm{1~772o1kF;5NV8;9=`5rk=BPIX^~K%D&{6Z}H34<@oK!55eCUlUBAa zHxm3z{@6d7{88>SameF$@Q4u6#`tXmZR>L5t^82{F#NK0xlsWj+6)tfBT9Rhh5*uR z!$Yv^#YDLuzIX&l9ZY-!;d0UCeu8k1nMi!}I+$hV&VR z03@9~1RtdT$rBqGbsD;pJmtvY4uHovv_{yNN+bq9XWoUR#-lQCAjC^|8zC~Vn-Kcq z(}d6?DYTA9W7$uLaA;AYxreCH2AM*_^5XOo`$^|2Jk$| zkGuZ~BO3A^BGPi10Pu884#xtj3*KcXspY(#5 z%*}<^yav}o8Q|QYIzjR_1Fj7|Lr~bj-_;?hujyN{M=bsT)8#OCvE6$!734$`B%9cLb5l$XQb_=eqC zsm*ESOX^GmXm=U9FaPQym1lujrubXDJE>sTLF#la^1rigHkTG`He)UVRQ{b!>#ad zUs(h;)Ke}XL$my{l`XFCS;r)Wi&_9yZv;N0{-oZV2F7yuS0B-f_y`p&6KD=i6f#~*L zC$ep?Kl;O8$g?TMm%Abw7rx0mf)2ttk%fDI&-E+byIX^GuGu?&k*pqQ{RnsJ4)Bc% zF5v`h6(cWGhZyK<#hF{_vNQ;@vu;01jWGd6DH3dSG!*+}1(~l`?1Tfp-nIn*UvGPz zRM3DY893zQ#x^)E1?2|({urQ79YyyYpj*)QB0wnUYX@-h0;}n$K446K&d2yetyHyr zY^8gLF5&a|yC%qe%1-#>BvuEhzBpkSZEXmPJH^U9bTRag$@DvCNHf83sdm%|zr4sR zpP|H0U|qTW_!#V$JL0n}rlm_IptGx?A6%}9a>~OF2ZTHLjeAMHA8$LT=?YB%u>QC4=t%L#ItstK+XZp~HUVzES zi#}MAeLa;Y=#XLCMB1FpF^`gCW$Jz0*-r7I}bNCKDsdmQwfOmkB9j(fgj7JW-B}$qIZ7ZyF69jpN?U-NZGxiWoGLb+aX&{gFDAjGF@6i-U~u~F1i`n^7=u#&fH>)y2T+a|K(QQ$(lM^b zYn#hj!dA#Ox#$?XQE-;c7*o#|!NJZS&$JZ7jrlv2j}{igqV4d3m)sBddNlrPEmzcoCf^@et6fa=<-LG_$D^X4tcRnIBKI|$?HIr82s z>~}(chP_AM>x17LPV_CnRr~%7eZ`6P^zsfY7y1o!l*^%G+|0YLgg9p(aNmIUU>#~r zL8%aV_R<2*qJy4WO`Uz~KvyAfPkoRReA|f;!=Eg<~LwAK%;b4*IqOU;#nt40-JR)dE7$ zp<05iM_enQsfLBV@GF8sxqwg;;sH^oa0i{v39rQcg>vLJa+}udB;K1&)D}v+L&q}Y03LVP&dNLUV@1LxBW3tX;W&*7Q4=%Q%Y>l^7jzMg498;=^Xk%LBWY7#} zhK3Rn{dsCWp(4~S@E{FZ{Y`JId|knzbc2y6IpOf_+UitXsFBSf?@^>;pKo8Kbv zD~22S(cSQ)%xv?!CySr)V_n(&xL#rM2Qks(w=Ij`w+7&MSr$Lu<7M(!Xz{yA^NRw~ zyb6BDnM{EuO8Yqt0i@w~CKeb1&_uZxINB5k329D4xQT`UG~qZ9nYKsoIKn+bL>m*1 z5Lxm}Eh`0%BLOI&bZut&qIG`v~EI|-5g-5P#TyR*G8A5Utyn-JmHHgcYiPbHGA@p`#2 zf6(*aC(g-rW{tM9>y8+h(pibz=i#Hbl!Wk| zIVZYpc%XJiG5A0wUAgZ!a&OHoQbtC9oS%nyG7i(jnR941GMxgRKWCaX*vo_bp|y|2 zyZlCo-@Jo-JqB@^o=IErKv<-~v*DcMM_Ty>$b%b6^tzE2Irm?H`0`vqw58X_a|Z~= zZ{p=gR6Y#xZGHT{g;#E|Q?5VY5#$T-&C`5^Si4Wu zUegz#yQ$->^XW`S@~Pd)yTab(nT5k#YdG|`y?5a4MeAm(lKsZR@>k#GRRd@DdWOh2 zgZ?r@2mB8JVCmscq=E*lMQG>>Apkfw$hi93-XE7<_8IhP^!BdxdJK#*&!DFmqn$sa zoXPN|VFsZ}+)7X^1FQ?Z1$U=-6P<7%o!|B@zJyNahJGx4NZ<&0NFJUjzLeos=Px1{ zy`8`3<^LnYl}h6L{O3uGk;Iz(+ewUTkQ@r~pCUE>MoES8moxGN`r*srA0{zT1r+&c z%fgw&fH?c!L$8Po$9uzwsE>*V{HFrXn~5KB0Rp|>zxikS#hlhZN}Q%y+m@}U@@-yP z=_{)6wO7oZiPP#Q%-Dr*j?b#R7zfp(TnLs}vq3!@B6)IgC$sn!1`87k;R1I;o zFBp5bYE=43@S7U)bo$1YuIBXDH9XbOUg7b+zk!deH?%Z13dTumk?o0%@}r2}$NeOd zv6OP~N{h}(%)Y8PBZ}cFzqv>qj^7DUS@yzZ9iPv5JkrlFQSrcq>=H{BS6LaxauM+gfFrSN1qDCe4TU~f_!iKFUnR>nm;cR}>z>o4a`9V1G_;mue`P~M7WpFdb_)*Wa z`7N;W2NBuhN7>rMA&+l>UxbJ@#%~*Fn_sn+KUAg1?^fW9!{c?OhrI zNV5U@g(K7cHp~DGKg&s?w)^1vqn%^s-?RqB^Ljp_cMhVN*|g#@~f)Nrzfl^XJ15c)6GaGiz;4Y@dR zIJ$V{>=V@zkp3M^7oRgP_iw#N&?muox;WR2oZ?gW$~BA zLD2Ofi03CeBmHp{HbZ|f;O8`=`!;@@P@aP>)*LgLN%X?tD1tEOZ=9OoI7u||_neUY zvU>OGEYW^oPyyDh#-0 z#LAk?0>cWq1h@lGc644k(yKqsgprN0Pj{qaO37GqlM;7FdsF0K(LGcAKG^^8SC!ta zTH_S}RqW-?Tj}z2nk;p}bGLP!e$I;@drjf*fAL7Zz-Vhm>fjh|aYq%b0LX!{GF;h3$bg#`saDH#q%zVV@(Sjqy7Pv@N?{3xXXEWf=|y zEC=&Nncl8y9ZX)GKX*aSXE`>pBfx9M{#RgU>?rgM{@z)ArP0sh%*D-p{1!@W z@#S)zhuS)Iq5f1a7T=Z^*2&Ase zMVE(j;3F7hh`AOwe}ok_oDRDH9a;eeVBDn3AWTRxEJ7eH+A`2kBPAE>U;!0uScy0e zjbL~QB6B+x`o@6#pvg$JqEe#MLXOF1l$8iN_|aarK$Lpr0Wl^W4{KS9JN6^X=-BY3 z5+9#=N<6)pYXZb{>l23)_0>|y#77HGMs~%+=s47CPIZ!xPMKu@&L`2encZDKL#RM- zGMxQXps+&od*Go!;0grFt8>nx&u{1x%e}f=SD4*S8$X^!bbsqi+OwcXErM#r3td^8t_@b`;yCQTV!aWO5S;k4CxCiv( zus1lqHL7OU(us^$Fhl36Ecn9@kkNTNq+ck^y!g(S6p`?)=|--96LLF13^68A6LV)Z+3YTelc>?GxpvL5%m4fRRn@EN zo~vgBmKf?YJ>RNVRlllNuikt0>ihduv2B8~@rl=vCi3~EA|;ie;(X)_SqV)^{5$LS z&mWY~)I>4k9{qn3no*kWs`VmYQYBO(ggQU2>bw9URQk)RyoCwwFcf*>?&wS6QP<4V^O-G)ciji|rwl?uThQ9X|IfqMe z-=LyF?6~>J?=!;~p4}J*$1sTY=A9Ph)UzKS6`g>088nGG+!@OpqoOPE|2VdQ z|164Ba>b|Rb}^o9V#`IY592_b=NLXW&?CR;hA|%HxpbjMUNrJ-^1s;B^8-pncS+uf zw2_6ud*yVa$s62YJo$+4#FiviFz?wq@95+r$cVfnxHC>sW0TzPB65z7txR6T#ES!- zZctX0{5l(Qsg7NctYhrC!NZbwVKU3u6@hvLElPH<+?6`<(&X1!&?+6fJo!B)ULAa1 z@~%#nGI)VbT%Ejzd8-3n6I{ULHH>W!+9mO&N#2=}u7G!Br0l66c@0Hx4tTRg%DxpO z*}{=+!Ba>cDf?+GxtP-aDmV-66DivpOP7U#(f@RbI)NpBjgeWWznS1Y$JWu`qlKBW3sd$?s8Iy$*lMPu{}#j1GU+Pu{@z1|5FPPkxx;Mjd|A z$FmeA@n)1}JJE|ifCTa4I4ckyF81PQxZxAL6V@R7dgQ7g`46mcHei^|`W(w|3u+~} z)r-@^2bYMS;KkV`c?ooQ9Pgvo(N$06y^72912p1^kMpFj3RtEWzaRg@nvC;K;@#tq z;q8r1!^lxl-a#j^_xk1;^KCi)82-m!#6R{e(=TF3>=&Kfg|SX&i#H{hoM=XILD?h- z#2F;+MQwvADilqe#G|LGP&_f0DNj?OlEkNdgicqX(TQI$G*yMhCgce)O@+oK9-z!K zRA_wSJcjgOBjt_%IBMead(wZK&l{L-Dt`X@3Ew1~DG921wn#bl7dW3&E<$0Ei4nGf z?|WSc1xl;q;Ox=NPlI9FJr1%D+#tJv>D(0^+dBFyA7(F|*e|A{wyCwEyu6~hskWlA zt`1v=wajeIwq|B#YHFJ*=9E{JS5=t0cs2TKM#ym|pbMurW-%a0b8gj3H?(3RwZlte&*A#2 z1z{QKOiQM{G1FSNZ5ZnJX_T|Isk72cZ>ekV%(m3(q7m$naECr1bDO=VW-$tBP_=8D z+u@=0F#^L$AZvIQ5akL^!ASRr586f@)XjIH7zYQ zxK@p=@OkN0P5sFcIT7aEmZ@>}+>?B0ppCVtNpqGbj0%NQ2_T7wCrr1Zf(le zcJyVpfD)QIKs>U~G`7|>lZ*7yx;v_XI(JkE#iEv(Enb>81x_7Jg7#^L7LRIosVmR6 zsa2|8U$c!Fwst}!uZy&T>OeTqQQuVG)Y=3QWH+0eSc%maELoLavuw$_H5aY8cv*Ta zTfU~=PN~ZTdfGV|)gWvqDJV)Zh)HoPy=5S;EXhn$OpM*^;MYdspVtGyk! zKToWNw-p*UQ`3r(cDkiztL_1aM=NDA_0T>-($4;#NrODq_{6EO&ma_)?V{xL<{I`% zmVmZpb8V4RYG})L@C?v(TC-|9j&yDFCUp}@tD?1~mD$747dNG;sKnb}9%Hzgu=r!^ zs}@?f8l(z3U#1gXBSW$F^y_e>F^jcsP3b`1d+Dy$u8s^g5y6uLf}7Yx=*`(K6eD*M zL##&gys8@2YtWt24P9*D2KE-7L91LFB}$#4?t0SdY!{VPPD(1v^3r`BS={UeQd6_@h-*z<7eED5#Za?I1@)+#X0O7454>Ip$c3r&XlL4)yoxLDVaOYeW0Z&4zfc}0#9Vn*kk=P~ zKwia_x5ud;zcx($Zp@Rn2J%ukW}GQ&2M)OMo^t9}iZ~Y&^U`rHJj z15VRerIG3*JkI9^k@qoiy~d3icWV5AM&VMBZnqA9LE{q|zoYRd8hbRprZJ9=!E%n* zNF6MOFV|SBu}veb0vNwb<3}{^*7$ji_&k?%-`Ciq@s}F^q%na`PkAS4JX_;CHLlZG zr|~L{pV0XC8vj}2w>AEo#u9X8k*{%{#-$q9XxyN&RU_wbnD4V1pU}vOPac0&;~zA} zaf|Ty2^!DV$bFJ{{Kpz!()iyRi@=O9KB;kv#yJ`nYrIe+by}G|tC4GLGklB2k81pT zjgM-4TI2H?U()!7#yC1K<&4uS}c<0_4pX>8Q^UX3>p(MNCAc)LFSpvETNABg9BmYb*GF)v{> zn}=ns)bj?&XBpF(PoVY!*L|2XcV5BvbW_k~a3Lo;4sWH`m(F~7T;**&8&dc7j9qkF z&*(k3_3WtJu{TKV*jo**G+w>^h18-CzZBhYTaVXvThFdNyY_BAf9DJ1z@PfvSQD>u z*WQvvJ74f&M>b_NxYfP4^z8L+d3p4rTf_!zB#w{c-la>`+S9Cn;l%soS^LllGz4c% zuNjd`H9Gt%dCYi!%;Sh$=5eHE9-&~!>yj|UY>6Rey>4DQ)>qq#T`hh!{PN2fc?aV{ z?ZB%TiUpSdpdF~V(~lD-Qk#VAh!kiXj4P)1`UrGyGQ^oCSbNVFutA6^%p! zH9c57p2d|-WvVi-_}j!ODioEA8l0*^@!}GuJWYj4igz;Q=_)k3SY9?$RcLInsHIL* zp>f4OV#+gAXngV47@Dp^Da@djc#OMJB}xqt^uBsx8q-t{13bsw|+}7M;MhbQ8(eJ z_jUXN@Bi`t5i{%P+{1ZueK=R?P?3Zm836;X_-*CDH-b`3pkCC~dS145ZCB=!O#Rx8 zUFWtpT~yP#vZnRiOzn9!?dP<$pI5VOWmoIUuI6*P8ZXMUT~OC~es=Rxd?kCQme1)( z@!zR)>VdQC&kFygPKDwT$Kwa!e9ynS6dwpFgpK1>ly&oN4sMLx5WPOOvuJB+CG@j# zTy_5Y(a!9N;gd}Li~&Dq&V&cL{{aT3FWww|1Pzyuhe z_iUCV&zZOCLje*w)l>l`Ig?A6BuKZ`V0s$jl_!*xr)nud=H|QtQ07*EoZT3n@~1nG zlWxZ_SI?9EdCpX1WuCCSU9cSUNVCeTymHQg)MskDFqPBJW_Av=b=9)vJKSS6n6GK% zA(aPGyz;t3unwQwzfG^Fz9(Mwq1%p|Erk$1`|!1bhdzFB{7%(jc@o0^Fn+FMTDs}@ zG5uiaaEpq3fH=y#SNjP@-eSkk;};-;ceHWFn?HZijq{-LJI|vCKXV>y;5T_FgE}(L z(4(D+<9CnSE{|ZW#q**(qLIgW3FcAH8qfO@PW()SjWhE2{HK4mj(7vn&_xEHTftn+1f~N{)>x@=zD7Q)7{5&8YK<3Z+@P^pV~0i# zV3}{1M&4r#@7DNvjZbR)zQ(;8f2r|L8b_n^G5^U#bn=NBPt(WeXk4UmH4*J{p$@Ot z;m=SW^2;|O9u|XtBQ~ifFYp_2{`|@+>er@HgYQN3Emi`zWtD)^n=SEXzFdj@i2n8+ z-TUkD-RCXp-tS{QlTNIc@>u1w`;Ngko>#ede;d{bc^qL6-+ui5i{m#|e+PP%Snp)f zS9?Zd9TP8geNR2U2`jN5Q5;CU+ftNKt;^V;%ix;pZMuwR$YvQVtFSU|=-B|-)hMGx zm(l0c3y5lu-Sb`=JD(`tN-_ z$z1i{pP`tzA1qOtO*!hnfmQ!Kkr}P}?CiiLqboA1INg-EZD05UWwopfvEomuKI7_s{aO` zR6^pSUmS$$zwcsmmbGA|#h|8le)gzCRC=bxkg z8|0|}207}#!IxD%7bd>R@=|J}#(o>uu*C*JNO6srI3V<=Srr800} z)qiI(uw+IO@OKc@mELuZ*!A_1}F=-&_56HBZM;|80Vp63nb7 z<%DC66|8Axz zNB!4lJskDl3dTF?zZbJCNBvi3l^ylpWsG;!f6wKmbJTy=F+7m^Zvzq(s{R{5{r5Ij z*sA~D$?}J){)?`9BJWjPCQ<+WYo7EWQvZ!yhYA->|4)1hltiOn#@YHs5h}tL&!&=a zWG;>rHBo2y{W!Sl=CKq0~H)L*97?;KJgR&v##K+>t{7i@OQZFu#-zu(STDp8L zufKGh-!^%ekLmj2=oaUt<2M!Gmc|(woF8`%d)+vX1*kmF1LMo^Jl4Q(^1$nD2Iumm zbHI4>xxD8wLC@#+kT$U1SngrhYi3P~MI3n>>{9G~}h|(ayx}K-jHc9P&&*K$cM6UGOsxuOHpZ zh@_sNag5{dK8@!1Kac5~dwUQGFq~^M>3)TDC^~eMMRzQINEf&@w{?b=FBUz3!_2I$8!Mz!-O;y4zo%LtWv2N(x8C%u5 z2-q>U4P8Bc*HV0_=HB$KSumdbht<3YZ~LY z9IVgr8mDL!{bL-bf*9jP{}}jJI=oAx=pWyyzbzEc(Yl(LV-?{xMMWkAb3p3>5uipy(e1MgJHm`o}=gKL(2aF;Mi6 zfues56#ZkM=pO?`{}?Fx$3W3P28#YMQ1p+1qJIn&{bQi$9|J}I7%2M3K+!)2ivBTB z^pAm}e+(4;W1#3C14aKBDEh}h(LV-?{xMMWkAb3p3>5uipy(e1MgJHm`o}=gKL(2a zF;Mi6fues56#ZkM=pO?`{}?Fx$3W3P28#YMQ1p+1qJIn&{bQi$9|J}I7%2M3K+!)2 zivBTB^pAm}e+(4;W1#3C14aKBDEh}h(LVTz3tdG-%i23*qb=O{6pu-D^h?nOJ{u$(RjK^y{?PYegL)obr9>_x=j}Go11$JiIlh9L4=)Ipv2DVL0W7 z7-KnQ5>=X0eiSkNamp%J^mGVvIOQ)OXgTFCGcM_vVy;%X0eRl({%i5OT^w z=!cxLFwFBO$@z~>2zMHC$|PMar~GkB84jl`t0!4bSybQ$;go-k3oM+nv~0*JbCog6 zDT|rMo8Xi$fVAG6avd*9KBxQ~3$~o{Mg)hzDIY_TA*XyP+i@72@+8(F)8*d{0o*Ba>}A}6>`e2vh}BnFgU0DGsr)(IOR{G zM#3ro2T$E{%G4zdN#tnqN;eo!cCsxjrz|vaznt=!%xgL28(EO$l>fknw4Ac&s#s3> zRK{9Pc@oR*ms6%`h{GvA#l)6V7Czc?%0j?Q=Z6555Ot^o~1gR@+zjcobm_;EvGz=O=LOc zPcqhW$~!5`a>`FI)^f@(FxGO)ZLD^ooH7@>ayjJ^*2CeHXE5I3lvlH?0XXHiG5&CJ z$}h0OmQ%i$vSJq?C>B zIueb?PbJ5Fs@+q@13bzoyGvIc%;l=gkS@ zJ8;ZB-#fV)dIQXk*HW9Tyj-` zT=H7<7wYyI=hodEBf7^A2bauiX`Nd-W?BycmrTB$j$>MK$<#G5j{RW@evC8mmXI-2 zayW3^+@J@oX7EEfi|{+jBZJ4rr6iwrlt)(WaFj<@?QoPwR_!ntkGz%l559u=ei|N+ z+>vSTG%Rufd-}|ty`WGY`4MH_9`eX1pq8UyGo0d{K6~!yDOvQrS~Q!GH1l`{Z)AW)n_Y0Xy6IOz5?p&s^#!NUM`k zwQF(E#>KxHF~N^iuuVh0b6Qm@pxX*c7%e_Y5$)Iqr(f35*J(Xic80(ZGpt#q7 znn!v&pU=We?)5d~%Hv*@{Al6>jM58g#S;%8g9()+);poXxz`^e7r9qirC_!>578@{ zV@mZYxk`oN3E7}!t_qbTM8j#G3XM**^HR)Lp|J__(!l~18keYG=u8zFpSYb(aFzhuo_y$}l8*-ZLSM z-0L54iLE{F->`a(x(sd48~q9k&Yp#YvYii=zWU}~bv|zA6MRwy;)(MqGUQ%`xao&` z{X16WA(d}jLM)#{d)@@=5OS}Zc=93l`f0|6-0N#BZwTD0DsN%pcPu@$=iR}})(`h8 zMy*e)e5(_}Fg~L~)rm2zS;)Pn849`A;8a#rbFa+cZ0Exu+MC;L1m8unT<+B-rUlv< zow$(gd~{N~olhtVr4_uL&mE|daIa7E4CV(E!aYaQtgc~Kqsa^1U_ALLUI`Ohl5Aw& zvvuCl$ulU!+VcuGc8-p%OkTyrmV5mVHsn$ryC8WTV=ec(nz5F96|KvaI`Puv!z{?! z^S;8w)}HsbtmFkcadonpLCd{f&cv2`<(=truZlr0UYl2G)i;qc@qrxw&z{M z({Z@h%OFO$S9t(f?)5|#Kf+h7A5BhVwnDhqpEIH5UNaP9xmO`i`{!PR6q7sLt0-q) z98A)EK#YYg_xcW|w%qGS*&>#EeT)roxo+*{$=1mcxpQz+Vk>WHQej_*(k%~UiE{c|LuH&mr*db^Em@!9ls0( z`3q2GHP(Tl?r(7|{VG1L{Oa-dA;yzI}<-ww6Ebd^YJ@-QFM@hiYM<6Vpc#?Ls;MDWdMoQXRd2b{y+Z*d-6 zkCo>^`xBloUZ%bF#5pE`bzY4S#2arucUXcqFY{9#(a5_}=a~(pTLeB}hVsgPrlZWO zG|EBzYwjZ*<67`@&tojg#jrriMODLc-v_^`E4(`ydl;!5W%8BQnmE z`_DLlA$FTN{vD(%#9s9^qc4N<6?~~G_KdUQR7aHAJ8cF zu8^}^hsE9%;U{$XI~v8_701Qi6)5(uz&I`!>ml~8z$rRRWmg`TnHON24vW1j!n<@> zcuItYrv!?|KGJb*6l=cLj>QD^Rq^fnx6p6nj^o*t-J7-W4eJ zu0XMO1&X~ZP-cUHV($tRdsm>?y8^}D6)5(uK(TiPZs0dIQ0!fSV($tRdsm>?y8>li z8YuRzK(TiPioGjPcuJs5sQ|^^6)5(uK(TiPioGjP>|KFk?+O%qSD@It0>$1HDE6*E zv3CWEy(>`cU4dfn3KV-+pxCQD^ToRfnx6p6nj^o*t-J7-W4eJ zu0XMO1&X~ZQ0!fSV($tRdsm?Flt8g}1&X~ZQ0!fSV($tRdsm>?y8^}D6)5(uK(TiP zioGjP>|KFk?+O%qSD@It0>$1HDE6*Ev3CWEy(>`cU4dfn3KV-+;1b-!y#8YE3KV-+ zpxCfr7IeumL{xSQy_ub^}LYnP+1LW~0-PW^x1JcgAEmoR( z-@eky9ln3v_Mdx`w&U3L*eeGPymDe`_kP85R`2)rfCKgRMqWN}VC3>ysNd-B{a)p+ zo|}%{#k%$E0{$!V@WlG$L(RI#Q$X+~5@+LW6g|p^dM71)f6VE4S)H8AhuRg4^BK|H zWs*+L##a5%rx7JOvYAEN<&gPew5F@e7@E8m|7p4! zeH5i>`joTaA$@u*ic$1wC>;lOZs#PFpoZ+q$DQ3&nUg*aXisPF*;O|SmDGK zPM?y4A$=+@$Z$F2MzoeSU2R6)?Q+P%I?h!!DoM!7&hu1gbmDy0Av9gRl~NX{xN(VM z*7i&l8lU(U<8n<`xk9hdr$P&d%OMMgWS2w!9Wz?f)w@{YAoOVrS%f~7mJR7sdB*`cwtt ziNB-BkUo8xvihM8hN6Z-pHg{1=+m3oxYl%)N){nO8ci-|tl>-JNwL&5!IGpHr&`lh zF=@8+=}z`TYr1+JC0qKGCk*AX zw5F?3O1AXr7a42mQ{I^_eY%%nhdzChB|G$~P+|^!D#M}P^yxb&#-UGNfEb}qC?!scvT(xw4Bl|))c6hyq(dO zKK%y@xAf^e3b*vBFuvAwRhHAX^y%-J*wUx`ly>OTpRioJ9P$!gR!g5kLECfb(?=L~ z=u^>KaZFb?Q<_7cp38bW^yy2?@0hOM!{QwJ^d;sWfIf|U9PKg``t*NUprubYvp$2+ zryLgaqEC6R8v687R&sds=_JU;Bcgx$^yJGi$np8%>7U?BYGmL*2i)oBc=T8v^w&*j zXodeC9=T)!7Q973Qe8Ts-gqxQh2P!&IFVRA5lbDPxSi>*IOSQKfIpssXmJiVD$08< zRgLH%6=`J^{IOH{Ss|b9-U*0(3p20b_tKl-UXS-9Bg-2rBAw-tW6Eb%L^^;Ckul|w zlULqQ5!t-*owF-uR8&r%bwZ)U};8M72ytLA)M+fPhehK^TO_9eV2 z+?UWh4aab~;5u=+xIT6|-JWS|>gde0r!!ldI@{W_brMfWDkb``c4p|zU^?AV-&Eh! z+JwofmfGe_D@?lZ{mA1DUb=Z+RZY6K3%>N0x{V0eHYmG9EWcdS%nkT-?B<&0F3H5i zj*Xkzk}tiIOO>;xEmqu3H*~euVV${pOvafNYwNOITu--As@785 z(cYQeQrBTlu)S(Q$mFRnX3tswI9<276Rm+1a{9gbyVj;`EvUe>S|qonR)ibbjTtON z-I+;q1z_BtOs6+aOsj06g=PcRw^j=+3$w^%vNK}t_Kx8~&1OqSsZ?ub3oZ#3!p5cR zYUawp$gQ?%w5uGeQ^WeriilK81vj*IZfnCnf$4^}Y)2Y7>zcD28E+U=djrd-1lWvc zyQH2A2(u)taRsH07prb4^F5;=OxsP|3>YFPhw; zE;F{FWdBd=R_}dNsLmNyKvrDFwU!30%Vsw5}OAXjkMh2h(g<^5Ji!A5Rf3?Qk(faz*7TDS+~ta)2L;! zSzfplF`hfuw2$_4SpsMyxyo1%vC!FjQ4!Rx$=Iixl78U z93!t5ev=1YZ!3sRo||#zeEEsx%KMY%E-8>#@_-Db5EieHm?m_s`S2)Ed?p35a#RPz+ zeq{Z^J_Y|s%vC?yM>?igBO(=91)cA~$$6UU4*<(jZT_xR!9 zR?$5j7tLcsqrw^61itW#PlX5E+VXta)2urfYX=){mCqJ(tuw87d5%=&h4I!^un%5x>p%6oj!^@O1$AYZ+}X-p5~y-E~* z70Ble!z(nd*SJyRPL0B^BHc%Hc(+F3S8@Ca9sZ6+P9sw;2oS;7Gz!0ppjK;c(`!mk2_Uj+)k3KV`7DEul= z_*J0rt3a7P1`59l6n+&b{3=lRRiN;zK;c(`GARk<6dlh`_*J0rt3csbfg5zZ@T&+5 zzX}w76)5~FQ215gt2+H3Gz!0pg0)<}%3cm^zeibPE zDp2@Upzy0e;a7peuL6Z%1q#0k6n+&b{3=lRRiN;zK;c(`!mk2_Uj+)k3KV`7DEul= z_*J0rt3csbfx@o>g0)<}%3cm^zeiisiT=kpZ z5%FM`^@RC=#{-=GX_m$n8rKo=KC01Sxu1}p?^)VQ<1sICL>&H%QY0qi=-Z$`17Yjp3&UgW;9eR7w5 z*m5R4vUagiULvsqkH@0Ew;wIeW$V1VdG`3>y2vYIs`$|xfd2jX7tr`2v|bFT?FExX zzLeuJ9T!}VkRK0Tk(5zC7Uft=qz19zUKP2Ehl+!ZfOv6{&-anP$j>QmeKF_1-oBPm z)oKItK(<#nxU*E|O3S=ZsAm5TzhXs2qn@}yLwfGK4f~LBtn$**=xVCZ^Bt&K?{DDS zjGyX7@ZcqdpKe+S^Pf3i`t5Xz&XhYh5Ps?p{p&savXpOKD>gjNkII}tA79Us_f1Xf zgs=PFU;0Ns?$(@FUenpx-c;Mwi3yt_INpK0Rr&m)tHRIiR_alsY&!5L4Be}7+<6GG zFEc;WF`WN>cLk2QP96(5kpF$xUpkJfOdiT%I(`EgXW|y)fblbqV|w02#+kVM@4Mfk z(i{WwJlF>2JowBpdEoWPVr-*FI}?}xeYXpZWZH#j(9wAnH3cUjQdzyjQf^z z+{b%Bwc~F1_?8jDz`lolrkxq@4*O7uLno z(e9PkWm{T626^S@3~%Vy*SOE^R~#46os8cI@u`1%@&3b~JP&!(GabYE-3EHJGjWR%HhxAuj34hJ<4j!sxbG!Yx*LfG8~0rb0sIm(&dAFj z_uYg>GVMY%?NSTB$wL{1j{9zdywONs$hgnFZl@KiKWmZ=i8s6(4Lyi1spgU+!VADpJ_EuNmnI8TZ|cbO(Ff$M19FoKrG~1l{9@ zW8CLnlbnV|Z4SjeJ^KzF`y9u8$4N;2V}A|bi8Act8%daYzQ~1BI2>cWi%=HFfSgDo zHf!WJ6T{bPyqSppd8X!*ZBClupx{Q z)PuP9T;CBk=6g5VOh#nESKzZ_zAG4t1$=Q zqB!8EpBeLInCM);8$icisK$ItH_(eQ-y1+o;&%c9)dLRxm@jw87x|j!hoe4ppMK~1 zSX(pZ1D)8%m=6bsGUR)c^J!U*UG02c&M1#V!6R&_2hAarRJ1Z zl~<*vVyWy5lyRn|rY_0WXD-Tiwby0Zx1}&NO3kjUoO{)rnX{&O=kU|9CR0<}jiUExo+wj?B#x{R$V?Js?cBB4< zjQKbpU>whxjt?>SeY+C@(=J5QF17HRJn$Ag=HuL9p<_Od%Z)Q-<&XJ3>eP?VB~!or zF&|f+OBrE6BQJl<$GJmOzfwnD{#+jy&^nMOk23FbWHWkpfn&aR7dYm-=@5+h_?$M* zw2KKFbdMj7F(3Pyb#Cd0X3Uq+h4ERzF`ta{^s#WvM?DPA^x?H3Bfk1SpYdKJ&V}Q^ zA&mE$hBDrpUs?Eg?t?}?Ntl3SB=Bw6tsAN zN%skT6XV_jJS)+q;MCmcBJq13O@RCp5EDFrf8jVe`Z`mpb$ste5)7F+^8iECci^Fx zzm9Je5^F=$n;@khLsVS?Y-v!WHAKCanI}xef2_lqSi{idwGx6M>R+QYZHW3mC_gkr zl}#hZ{FuqO5u!Fky&qm0qD}_p8KNrr(Zr`16|UpE5U(H;DoIRr;tDrJJr}uXh$>4y zhU@sAj@ky{I==5>1BdJQu3|ZJRgFp#KI;&!<2#da;X1zOQ%bmw?>@$b>-dVMRPH*y zUqmrth)T`RAT&gs&JwI4>gO0~4N`sM4~bAu1J7?K-|IC^s}jWnF?H z8KUy@n1-mYGG}OrdLzroU&r@x7G(`lZ$X;A4N>)F&t1n?j1@ye)K5}YKZdAhv4e%{ z_`b-Bg@&l_WhgX6y@x47LsVHwG&Dq&eJ+P!h^i{RFd?Rop&{xkETJDm)RAnYr&Yey zi7uuL*YW*Fo?mE)x}BlW5H+Y_(!tm9<*MGfhNvvdF+^psKtoiW)Ec6OqK0CKT8}dp zL)7bd8SFZ~=dmS>scAGRHoztrPl~pl36>U((stsyG! zOxF;#n_M5syqO!A!@*Mb{*eJ1_v-i z{V`fe3{ii;=CbSf-oSj;5cLjLs((Y&;Ax(OV~BbTrP+0SpJ8@uh}ufw)(~|GW33_T z|5BD+$9FL=jWtAloQbU=sxV`A9bZx6w1%h?nAaMjp255uf{}5)!nXU#YZ!J6Q4#d8 zhbnteq^#RduH#uahNz_fT|?A|nBFl&-NfP?L)0gje*i<&;Db2Tp%|iml?7Tu)OoDW zAcm-ukY&UU{Bzgw<-KYQQRlOg!!tx>Lk(<*n!q5(r{;;j%$L;2kirj!sGwn&upXF_ zo8Wh#A+Vz=9?M@v6H=`6i}b4`{e@~RyTBU*xd&HOZ+O#XuwZKLs?St( zZ0qQcU2STGja74VMQu}SMR|Dz*8**{an&Y?@` zpE+KJun6}S;=*#a1fO$F9=fhrlUuXc-!2bgy48V|SS=)tx4bgc{z zn%QTl4Rb^rdk&rEm(G&zW$HS3fSL{H?~NLPrS$Qw`$=GWF>`oUAtU#6K#IQ4T5iI?YQ z+*_G5NZ};=;tbGC9^?39JoV9xV}FsMC*JsqQ)p70LUT9{r4n3cwpooxnVxzjl|;;+ z^FAHah7ey8?9nJToG3?Z zIDulr37mpX!t%w26DT&Ez&3qcY&a2?nMt77a011K6DT&Ez$bNjvEf9xM~B6R6JfF8 z1d0tOP;5AXV#5g(8&06ua011K6DT&EK(XNjiVY`FY&d~p!wD1{PN3Lu0%cYRC^nox zvEc-Y4JU8|Ze*Uf*l+^Hh7%~C|3LZh28sy?CC^noxvEc-Y4JS}+IDulr2^1SnpxAH%#fB3oHk?4Q z;RK2eCs1rSfnvi66dO*U*l+^Hh7%|@oItVR1d0tOP;5AXV#5g(8&06ua011K6DT&E zK(XNjiVY`FY&d~p!wD1{PN3Lu0>y?CC^noxvEc-Y4JS}+IDulr2^1SnpxAH%#fB3o zHk?4Q;RK2eCoqZ^Ag{OBa011K6DT&EK(XNjiVY_)--h!Tyl|L*95KK%Kx2yi9AUm2 zUETc|I?U$<$(%qDY#xdANf7U> zuN3Qb87?oHcz{tM&-V_DhU(Stg!aGq}wB$DSlAN31Dp063LWqH0!7#H$<%UFky z=ldf=AJSOu^xPowrhM$n&u-L*w~K_>$-Q8FPj_-_0x|pXZA(iRJljLz*G* zeAiH9$n(kQq92~`TK2<`=lge-8uENuhC-h2-w&-Vli9)Rb&fYKbE zZyn2a7M%Sx^E*6WD~oe@zQ>qATD@eavy0;rizAVt8Zu|DfvW z507m)bX*@kB;Ews_x)aEA3k^bwKGT|&TxAC_MuDZpE)ji2#oL+;o@qpuesUdr6$?e ztmjq^3EMZki({!EY0QJR*z8ft@F1-ZJ9Nshf3~n+rY~m|*Z~BYGV(;Vgb4$X3} z=7BM^bzhGXewN@nVhBIH5jYo*7qZNU0c1^}Mf7Ngcf2@IJ2_krxiZe3@two`Ooyqx zUbf+#IOaO0r8^v)AK!MynR4?vzfPP-A)Mb-aDMZR5C}Eie9rG`hf`yFn0Bdk@~}=D z@KRqyk9MY=^Etnrke5Olw(~%oUpM4U)3OMre)*i={Z9Qjt}yk>w-b6Aktqt&&dA$= zu+yyGry$StgMm1|mm!b4I~YfK{N1O~9KWtW&hMWKyyWm&W^z!UK-yeG(C~6Q^jLgqO&h<^3GmZ_xD| zj@tLZ7>-TC5_glk|<^3G{ zzNN?(@_t94eV=HXguGt|@9EIK?@H9&@_vQe_lbr~$ou8m_dU-Na(TbdzV9=P3wggh z`#ztQvAka)_I;ZmZ&3Tb53|sa_dBHaeIt1amiK#8_IIHe12KKs z_X$5}dB3Y!`T)FNfA)Q%>1TPr|6rvD;Qa=%?-PAQ%lkdbN?G1-358qUZ(#erk<8mK z?>992zOS(00eHWB`@Sn!w!{1Vp7|Z#?~vQ~34dyNzxS~|!{PnNof+Qm(A)QI#4Yb9 zd_KDAQurzRzL6p8_Z*KMCrKE+h(T@NcdTLkJpWk3`OV^q{{`6hy(!9XB-a#MwaGE> zllAlZ+ed)yZn(YvI6uy};9|l#_I=Hvecz#E{0#qQel!pNV~^mU zzmEUP{Yd~imhP`FYMJHv(h80$r`UX`Zzps?;H3Z$R-~LOo(2wR~5295Ss?`IZL@e5Z|ThqV0i?Mu98?fLj4S zeLW@2f6Qv!aHmVyC>8ol>=r{EP4CI1J5}j+S)H79Fp(t?hbUYb_eQP z2h{CY>haclt@vu_U~nutm=5S_(?d1N!w3H9_%Xj35%-s_5@}2xcs-`uM2~hRZZX2f z&!{ipw}&3>OkBPW=%qN1ZlvLP&@PYAKu1F!>qU7Oa)tHF*8#l|@=}O5 z&dA$=131OdjdSW(ilgRy^S=xK4)P9!xl~(F=6(1X*^H)r@dA3ZGjR_hj41Uqgr93d zoMI9IO**K2hJA|Be?q!P>Cw)lBce*7n~Fx^!jOZl`$ByXKBtW{@=VyEd;DLV0>2nwPaf4UB-G2XK$cs$j5I4J`afVHS&IDc$vo4 z8ZXkwfeqtpG;Y+$`+~=>)p#=z9pYAvU((3?#9dQ@&j>8TD9;Ka?@i{bt;@D;a|W4% ztsyac-rU);ghT;*1MO80hhAe$Z;#po+{Yf^CD;$Vp!MMU7;h?Vu9!RQ#6IBeu=7~3 zev8nF@^*%dV+_hA8uS|JBg`0d%EqI?kttpW?#>W6%*Y1|8`=g<#-ugnI;!LD}p2AB2|q4Hfu0@cjWfMK+G% z6nPQUb8!!R@5N7gHn8tOsJW<&?=^sI&a^gmZtQRCp`i~U-{dG)wbjtap{fP?SS8Yq zWJ96s(nohLI(U0ky8SE4P6o$ujyndq8Uemn_^jsL&U6gZ_LX4_0mNOZ(SSS|5SWf1 z(+`%8?;eu}UXSwl{xHtOEye-kXWVo6?V(3I6UTR#dmh|pXg7}WJc{r$=aD}ay&D3) zg?QtPy!^4~3_K7`yAX}MTKG*K%BaT;Kb0QsOgmqRuv@<>$m9Et_2aYF$m@XLmA4D> z_`zzNiOV00E_LdMS8*sWe=NEi@=`_^(8${Xzgxf6PW=WNi#`Q;w9Pk;UjFXWXpVmu z9b$nAIB^fck0`z_#Ll*_RLHc$D`f$C;cFBVM7g zPUE{ZUZwFmjW-d|Pj_kjdwu+I9cH}?8kcrtYTD~Ih9lEK$E9bUIj_)h=|VoF(50#b zrsCV_IPdifX1?rs7wyLv^cY#+tgU;$cU$+qpt^fsWPA5Mzq@wf_uPNl-Vqqj7GXR)YH#+*lqt)G?{>^~{kNRyp zU-;F51J4v8P4v6Byzu%3UwY{jNz=Xm4+jps(t8gicUbGrvSPwjkA-7u0Gv1G?RP)) z{84XJ&LDWC83ZRzL$j1vD~Ds0m4mkcw<9hroSK^5E`F@`2lPLSf1J+6ICcj9NAAVH zXmL~zl;W@cnmd1Uj-Rh2s{X3m;9Yj!4x z_BCGTv(32e@GLEzvbuEYt^NmsnxPe@rAIpx$1%F`GwNalcxM@Byl3HnJMQ@?9^xFFwoI~O@Y>(|Er={%MVmYy76h z=QZ|d{I$jd8cT5Mtp6T{Ay>`|%`NAJFrSxw&O>)*x73BB{nX$#&2ttMyq57zd|05{ zRS8T*e;wm(STytH@mR-r^oCnr@G!>bdd3`^$FaWgsOo2XI48ZR^4^~KXvBMW^o&Z~ z@`8`OOT7)ZNSyC=?MJ>besy=m$2i}wz8eEqHCtu7bodNXxn#`@Tj*Hy%Zf3PJQ3B ziC?WA@AK7ZR8Imtjf#pVF=2qi(OC2!;fJa8ClC?T`sXp`zZg?o)IooAaxs^pkEYH+ zsF-WmMkg`Ci|@d{oADb#4<2hOSCk9T8;j{^S>iziqZ3$QfE`gk!_efl5{i0}Y6i=0 zXMXMv89j9>yd}{H7nhI8O3Bd^Sz_@qTo;*(2ak(QOZ*pd#oU#WW7C!VXyQ&r=}d-j z{G8>?QK4w!M~tgdp?Kmmto&RRDoM~HGB!_zMkoH7q4_E_HnE3N7O2p;!~%xSRH5++ zuH6$mONCP2=xNCKB8rLou_Xz9Z^S~2X#~8OT`744Gg^yjelEraSt+>(!f*3q>r~5z zD<#v|I(CVwLNxI>1%(#VtV?W2JEAUzw0JC&_zrW1Dz+ zMkn~C7JEqL8<*hQAQrBaJeG9`S4!rHF&0`(Kgzh!Vw(2dv7D8XV_#AwRF0 zofja4N`G0Ew=nTE%L`XZ=91B|9E<7L*Oio|3G(i-r&YeyiTCsTo>8IdL?ufPEvC7W zdF*>%f#jg4bV)|GHFVKmrlh-k?T`74BW39zB z@61?NupJO7dn!nB1Ql~Ernm4U9E<5cFy2`y`9B!GHuxdFf+A(F#&8MP+OZWp9misN z0}htNiv6TKTJ1{7^H}@{U$uTT$vvfG16WK?Mroz7$$s*6CbTOhzmH<9#q@EkLjM-i zQ7)QqR!WX-43b>lICgQsR}=dHwM$~wV)_aSw-(c%U}9@A{bM%o<+`<(Cm&<1T`74w z^ID7PI!d-XqVmNWvnwUv!=PO$xrL&f9Z{<(%dwcA$oN_*Il>)LA7o9Om6G32agN3G z?TmLUrk66_StKV;;nC|~O*JtfB{;V_PTTaG`5 z|M3^`uN9hwIRH8igJ3L;X+atC z`0FOL)M7K#5wAmXaQo;}WY%*eOZKZLv~5AU1Pcvrk58!e{1QH;g4;(;^ts86f2^cS zVfd9mMPd^pVtws<+Yt)9_56R3=0{~)#{TBmhM_h)yxBUlo9dg&EjznNI8HW> z-C${H$x_bU-Vj_bN-rYJBa2N&M=B`E`!~m3*+L!M3Ab zx}mkEy|KefW2eCSss)~7r>w+d>F3PWrp`(aE9+u|t(IDoaZ6o?ggf*BtNxoaZ}k~q}dqMiXxrS(bU+QsZYuDYPw7I+LRe&yC5(Y@on(Z+14~` znpWpim(6Z!!ujcaJVhj>rJGvXy4o{ou0Rf(;~JiOQ%AaPYYon@o(&-r)7Z+SJ=3|l zrn$c!-O*lG(VE$Utw?9iDxX!=)}GNpZuVPOp_isMeJI=|G~3Y7k)aiCEn5$5s9QpM z6*Q?4(GF!8(>|)@8d^~=HUq7o>ND-A`QoNVIh)3smX;ccgU?I1wqV!ctlAdUT1rbw zwKB5an3VNFF=~cB_jymM4d|1?90_(skJ`cGEQaUKi4LWHOr& zLOr%fJsR4w9ZZK_(3(}8gI!};nxcu_B$GkeY#??S^fa>!K|`x-D2>LdM}qXa)mhj& zd4f~lL9T<#Ir$n%yyv+wO1}eEWCMMRgLNm=(FjD zF1AIu32>`4qY|agtEr=&7f2e&b|g6!Ib%MV)Tta*ml~eBRG;fNyYB~iRpC8Bjs-84 z&^6O(k;Wv5U^_Cywc{AwIj_K&G#Lt!YDaad*W0W@%KO>KGR<68G$m6@-I1@)M#+CP& zQ$KzX7`X@17TO*lTQ6GlN!pq3x4L|^`m!YyKnB6~eJRi*%1_*#wqJM^s}b33_jUI9Y^)Nd(#6tSXV`YWgU= z-U9TO+4N{<x!HqLS^Sl(YjR&c=kcjKd z^9{ymUSlv`2mMia9pE(P16FAiUI$@5Q+S;BIB~tkjT(1q{D8)fXxy!l_Cw6~ghoyk zGW-*b!rLJHnhwWtnHYb(Myj1Ne7VM2jk2dJj{lVo@6sqMR^s?>9saz=CpF4SZ;0>F z;a_U}lSbLo74fpCEAVXGAT0l#8fBGDgzI!z_H;$~6FMw=x*{xlx&mcSSD@_a3M@f4 zr#w!n6J<|VpzP@iT%(U~&?tMl;`r4%Odgr}WKUP1?CA;=n{VJBbiC~8iZG|$nO^pE z1yuOUZtU?E-PD7&dBqobYxY5B@+5!sbuajPATz~$ zYxe(lKlyMvudqH|<=rta)g6ge?!bfkrkGcGBYxfcBj9ykKSyz~rI7d2KJ_gfKGssO zQKJ{}-f@*l9`;CP8&-5Mohd1fSR;+M}DmN;I?1b9D-kk%> zogK3iTaJif%%+5sKr#_x%LtFNHaUSl=7l6L>(Tez(&7Jpe~aopx}DB^MaUhGPKe== zxG8u#xXuJm$3eVO2TeBvX-poHx2ICmN`d9_>u~U5>CTZ!P4F z#xcs{aU+l4+OE7OA+HR_j5BeYaKM#U@6-?8P~HysO&-dimPU#m?M&RY2)pu{o%&%| z6w3P${LEv{u^1Ik86i53!H;P_K-}pV-!3o#pvm$8{HRi!iAp0~A>ZWJARVbLC3nnk@B4koW^)yrN;Rhd7tz6GL5S>$|wfMH|TJ?My^W3^xHMwsPS%%e@jI9>|exh z=;Kf5F#AJ6c1!Qcv}cE)$2fcLyak2YE&Whk{+w~UKl;KKU`I5I<6M9AIWPF$Rr~$Q zJum(dV`bVG4XAgRH_pYA&kT3-$GLVTCu34Sv@z}%*nIfmCXhav62IqhNGpatW{kUu zNn#OBd-QKp&(0QF48@`~2u6N^e;5Ei4+t&mi*S>gF|8k6bc|)ePfuCJY}F$! zCQRAP{}+yw?SQAgOJa0SIKs{8rPK94MxExD2c<&s_TqSPON1^3pWJ_Pb834k|k%Q zrh-G9mYP#uRbG{vij6cgurnHX(dpr@_50^Puq4;Rdz0@6x+?rq^k`?^e3KAnI`d{+ zYZ7Q=9Np>ox#PiN74X1tsjVt#+9_O-+qnE$@aphC!IE9QWS@A-~m8T$GA>+#N zYy?zjEMTB<<#?R08B-x?PMa}ooO7zE>aPx%522SGw0YzgCB0Txs4* zDO6gGEA{IDBTDl|I#gpx;a*UIbts!-##)U{8rw8((a65dbT<>x4{z1jt&cyh!@Tx| zj3J?Q0lgWmK!UO4U~|9o=N7&uQ#C7r+ptu$C+QB zm}~Hp>Sfxq1y3(@3if+>ZZ6kP&;JDaFX4?4@b!i-3N_Il<&3%B3~>*=vEVuYEPN&) zZLHRx1WC%YCo;Ks83K{ia{Mpe04b5aO?z|+!7?N%EfZyxSk7D(DoOkY zL-SNMM5%+A_EaK^Y=$T;8=Cekq2NnYg`$ae$_-6>SeIZ(rac@-#e+=Z8RiU4d%nOj z8g&`a?B*LL`g0a#H$(gq(p(XUX;0vq_5{0iKAQFfpHzW(;z5cGO?!l%&oS)@LerjA z?1vAjeB%;NuwtQU&qWM{rahlz%Fwjudu-FtwCB4_nPb`$1 zL(`r=vVsnah+B?S5OnVr_#d$aCF_>wOPHIhiLQxP%60rFHwdy?Ky`R@f;mnncPXq*0hHs@nETrU65>HVr$y-9D~-h z=V~TisS__vHd2%|?b*m{VoiH~!^Czo#79|YYua-^6I;_B-kI)Zh%{buH$(ggg*m1@ zw^N#9+H)%7dz<#0L@|zO&m%}Cn<2^rz?$}OshYrUhIko+1DN)F0nJhxz_jPnY#(dd zLy97>rakwwQvI9u1YD8UnD$_oz2q+#zF3>*cu9Vk2G+FaZVI=iJ@Rg`raiLPL zv}Y3Q?U?q+7}_!I`5PAJnD%rt{{W^vh{FT6IDS&xF{ml_;xw}f4Qx*EPFRER>j6xA ze#i=1)1EZzGl*%=BxD(}1OJR^54!4!yjRU;h)Gs*c&0s5Ar_B_{!M#MeisHgK8N)n z?U|T^92o9gjPm`_%Kdzk48REa`h{EKXTA1hW$O+4g{=l_Gl@+(t@ zVUpY09!D%{`k9;@j;28F2b;{p8Uq|OPH2|{zc$8!59d*zX;79s7*yg2MtH~i^CI&a zB8$smoU<}Aw>)xD`SQxjS<`2|7MZv*a?GO@kt_cNY6X>*Gb(4De{ba74UrR8Ha_;i zoKn*KE-X(q_k+jq%3bC61VrXEjJ+yIy zG8f3Vfk}($iMHy z=4@?Ev#WKQ7CR7YO=HbS#1tpA#;|G<{Ei82sd6l0@@-(mmP6S*Slta(F&J4XvkGN? z6WVHMn~!0b$b@DW#!!P*@5m?vq4dTqY`eqhbY%fk_D5)8q{>cE=sPoSd--1 zSM{=WF}7g6nFidUq2-P>V={JFEj3%E8o9=G`8IOeF2~r~Nu8B-kv7Cp);^&T70!v4 zP5nGL3o}8>e^gp)A#0?xrOq+KtIO8Ivd6U;6w5tje`bwwX!}EJAJc@!z)6`ixqv;nd+*3Ia7${^!r%P#1#lAD6#zUk(_Bh&vho@sbH$CDv6W|1cIm=4U#* zfO^^VT+K+wnc#FBV|t9N#Pyf160s%^6EfXjI|qz+ab7wO+4xp8&ZHwB zW5Pb<&irxc_`yuke8xII}^79VOL(8 zQ$J)0<=q88^YHr7y%&*RGbf2KSaxxKZOyjUUh`Qx(X+TZctq2H_`kSiZ#({)rBQc#!g5(->#{5r5Q{xPr6&G2h zuC=!pSK42R7^4aKdql)!&7J6{5r<_48vr07g|pJj41Nw7oMi^*A&*{W@NUNR%|%94 zt|*tLGF&8<8RV_+U zAQi3NdaE67TjhV>HJ?L9M6UmHSZl$+ZhCayQa!^use{&l)U}hS4ni)Kmu@;Lw&4<@wk$+-hYi5u@TWGP!FEO#jBKc&7j+w!S810xDyphZtERuM7 z=wOlj=nnV9B8laM4i>o`$?`*(85C2i#Ug1>Ewp9^kEOR0Eb?vCwr>b5lKMTN#UdM- z&tj3cvQiymk|l{s)9+xBqggM<%-|AobFj!m81G<_-0vAWSmYHf z&cPyo#r&OMkr9oOb{C5bfkhIs3oRDOZ`H61Eb=sD>D_=ojYVRp9>Ql8w@H{8Je8H) zJ1nvYY%xW2j75gakRW)Obr@U>c?b2yUzyK#&{5_AGzl2-lSG^RIYa-29#Nh%ykT6N z3ypEW68Hiy$^ZQUA?^hLxOXe!E*R;l?*@S#8fXxmqH#prErTQCTH%PeGT7m9e(5QT z7AaGHd*FbDOsO2ZALk}s@6YWU!?L9kMq`J+>C{Dzx=FVgW$Rpy>+}V8+JR#|i0MRI>^o zS`E#lDfEX}#fO9S9J4eP|KCG=d-nw4L4kS0QuFL6Gjsw&u9DUS_e9PN!uX*eLJ1oE zAIAw5fUT?oX5)liLVE{Ly^`vhs@02Nn(^a$%u&0x`rKClBnsiOalNTn$me=J-#dK$ zGaX(od*FJD5#~CkrOU?kI!ecNcb$j%nC^2W3_*e%ZL*4Twx>W}up11Fqfnu5#+v8Ls!A;CH8Pq~b@OTOHoG z^v(lT(;ZyzMx^TwuD2cOx`XSz20*VnxL!WS%Q#k4Io&VXuc;fpKX5&c4eNYy!V#Yx z>D<~t^rkA07`uQA_9>DQrKOC^fC;Oo?N74iK_+&rqFME6fzr*v$mksf` zNYMeL2p_5F2uy7~$M2-gJ;HKb;&(?)=(gRx8GPxZs+vVavkk`MCmJc^5>&D z;;xUdnvD$gsBDk14=1t2jp7=@Bd9+KF&ayxB$@N_L?(P24+JzFQ&>8RB)WJyLvo_O zAQtH2%@m{bBQ*+?U{U;KKMU3!b*W>R?`1R%mHXBLS5f9-4toS&S zK*dq4%?>Ssk1CEln?4*-A$))pxlJQ17R$`Uj>1vLAO^7FBzS^?o$y2nKHa%}W}@IH z@S}?3Z$N{?83LAlaX3O?V8!o)q4ve%_cDo!%a1Q|d`iW!T=3xOWnAo!Rl@rlT!J1RYOXQD zWxqzGXyb4%@df5X$wI|Z^z2y-;?3H`3K>{Y=qa5mbf}7Ban>a`j~x^iV`0BvM82`D z`1=Naiupl)zlcG7Uc_G|xKYJ%2y-rE2+QC~e&E2098jUq$z4H!pidE6a|v=96-Skk zrDmzm>%ONDP_{1wLi;1=N<9rqdc(01V)ixbMKyx>t;f;_z}cudw= zo?aFhSaAT$d*XhPuS_atKKWzeJE1R@N4?<3lFRfs&ua>PD%&?Jj_!tvqx6->4`b=i zPsH>P6wi(Sl)hg)C)$A1%VsfSPXB4(>N7(DTUS7D0lm0w)Z2>wj%0)tN5j*9MP_1p zbz)+Lh0M_}Iurl>*#ka7Ajwx;DZcfLXP?+|k*g5_F}Bgn8yJzla{Unh*_MY;BcbBR zbFiS|C|m0#uSQxZF$%N8#Zi)ohVjOd1l^1`KY2a#T3B&1SVG0|Hhg#E#KPn$%xhuA z%}hKkU~_}wNlBrLY+=Q1j6ErMSmd3Wyo=p@N+748vgA^hYhgu#RZwx1u``kbn0Hq2 zxX3#z8Dm}xE7mh_dBA&u8<-Rtk1K=EiNxn5`D8-HQ9PtPy^J$2R2&87UHn{-{4%p$ z61;@uVexA@$&<@ma|2H0LGj4sGo%()e2uZ64*H7jEl-YM-g&`#QIm6$SF(NQ2ak(_7ALVL;X%bw zdTXU>WrgytNp2vk${;Daw%$)}BCq+uDB-`>PflgL11oN2O%{mkVe#$0K=q*FNbX_r zU4C*S<7<@vUO&kt7pOSO_(%LCw?adWQTm_qy%-z8hf#FfAzt(?Bmk_)3YcK1I3DTx zq2hQx{6fX?kF4;DfPQxCA6R~EuuOQ@crjjZ;u7qKdNB@3-U8(x$mi%CsyOml#cldM zI`I%O3w|bGnO^LB_%}7d>1BL+`~p7S7&MH;A&4k+5J#`H7mSeKi144_KSnr;V@r<< z4~hL=1CE8*!@~s=ep{5B24?;rqtPI2#AgxizplK|;Dyk5+!ZkchQ{Ek3qAi0<;zD= ze*^y<8VK;;5}`xhMneCGWU(UO=Tu2YsmecpXg&+SFrT&e%c&W8VLl7>XAiwd#t7dT z!=YGNn?D_lQVZ%X_59DsRC9*YQztwroH5|}m*leme{K=}y%9OA#kXYIGm5Bcn_7kp z-^v$Nnhif@XQrHr!UDO?cYmOvU1;^V7-k0;&R*cDo;){dWr=`8H3W*=VjxglD+m-< z1~0`p#0^CVo~ZvC=!XR*v&N>wfhA#lX6%#clY?RH>qyGaGUk#udwSp4^*%%;3xB?c z+gXmE&{kZr0HOz-jmOPXPK)65rtq7A-(39M@Uc$#I$Ud)frn(OZb`UFosMRZ&^-=G z!?|A<0Rp|WW-m-Gf4q)X6E|HRi}v9US?ly*HCX0@5+eJ|r~ztVc@LG)Om)2;9E2v0 zzWOMRL+jw7>*BFhz^Q%AMEl#*jC8T}43ycReFDrEYNd8m=x#qb-Y{Ngo=>!F z9nbjT8&^A>?pOpk!QL#igW@IIL77sw;|yHuJh=2~Ajg+E0s4}dj?3>R0Hkae`H^aV zRk+r9aOrKs`toRAD93bk7vSgeyA}L&KXbWG^Q*(P%kKs7OCd}<9hZ&G-sRMfpC+0g zKNNHx@_45Mehr9o>vykHzrKjm{It#`dC?^h$$P1tF6(v#@LI=}A>Hd_q8#J+`<|jI z*gLGCeGKT&_|nX$*{&mrB@yiiW3BNf7+3U4|Xbw%I+_<3Jg zRZ{ke9?Q7&5*Sxwc%hu;r~R6`;rj!_6$^d3aMK$y;G@87O;NZAVIh`dgl;2##BlkQ zq8hO-DE~c*KB4IIioU97o1*V1`VU2gZ7A?5!XP34QblVN zU8d+~75%)TUsm)EMekAcaYcWw=3X5rRZ-I-KFTkcwku0C`G9*L;vZDp0DVIigF2;@z*FS%=y9pyz;-HC|4qxj^D$i zVj&51gz}3;JNSRA{5utWS5b}`<~vByl%l1I9;@hSiq2E?LPalC^z({tR`gqn-mmD7 z6#b>5t%|;@Xowe*sK27ciXNlrOhxA?TBYb`6um*wTNJ%h(FYZMMbTY~{$0^tTm?h> zlZuX1bfTiu6rHVTrJ^eoy4FX8Fe{`YA=PQFN1{ z;`n%^`WJ%A@KTe~qF)CdEtfS>^wY@)z^DK)ymg zmk8sWML)W$-E-F#P*L2%zg^H|9j1490us1kp zHBI2DUx7C?c)HI{x=#?g87hpy-CSxhKZc?F&-de*3ER+H1&vU*YcW;W8OX|gUskJL2BgB$TwpE-@1W;ULB9lAOC0SmS! zV`su^@YJ8kR(FD@p27l*nx=r}Gt@M#$=JJjPYs?rlKG69X2?1iHO+fis!`LtlW|5( zQ=Ac-p{Cin$=D(>R;R}QMQYSEg?Zx)HBDD6T8`}X`at=EuMNm3$kjOuQRb#)BGzFTRe3Uy;e>0 z>r8CbH2Gw@CS#Y8wWFqaE*s&fX$t(*QPVt#-1k(|l>{R8=1HO;%p#;R%li?JQ6X+q@>@wo@T3mHw|sZ_TGo=Wb- z%-WdUs%c)s)K*RNDkipSn*YfTuz2d%7;Duu`!TOo(>#Nmt(qoZtd7apXXv$Rnx~VM zqozqH4|pmqwsMO}GRAlZPZirB95u~{$<9&JyqGn0)HE+*yrZUh75$y4X@>k3?5>(7 zO~#6EC99_SW0v2AnkH{ShMFevR6eWPWUSb|@=>X2{u!nFy$1J(tmFR@we=q($SH*# z=w}L;@}d0an5x_x`boCOXl05I+56YI1A{U0bV6EO=icjh)sB z#bINjR}A3_Fjf{pD>77>t(DSjOQViKR%LxvVA!6q{Hm-CS1A2vX<98A)%>b(t@Gg0OW_4RnitA39bGo=Hxm4G zKcfnUAF&3P-wg2M`&>I6myP>Pa_WaHhTk=~)_KUI8j&epD5v8Z;CI_Q)u|sb9o=5a zyUfG;N4F0Ax}%dh4!F$}nMM5Ta=#59MA=#$RfBZh!SfoB?jfBCRP!UnH-T~60n`%D zBVNn?W;;mPR+MX8(*QYkb%5rl{hGSr{~LH7?}Z&-Zf)2ejwI{$g-#~AS-7$IZ%-$a zHl4L&zh>fjRLYcARrwDvFGC}fGA!{tw&%m>W2TuO>-?Wklye6Crzm>9qKg$>r|1_H zy+zS&iawy|6N>&^(QG{LZ51BFZD4s~c@9*pX@Q=n!mAXmRrGR2*DHF9qT3X`Us3Ge z6?|V*l=|o_N34B;{#*Iu81M`i3kRUbD*wrfo~h^oeN5R;2lO#zKh!BaZhz@x3cIQp zpZ^m>`52S$E2{Cl3>*$qnfS&Rsw#j-SwN9BxNDI2`pbrx1t3 znUx;(ETM;q>g7#I-w6MlR=ivC9!dYQxe_{=kEEY6x|V^$>i&M<3`m@GNhL_H=lYWl zMwfL4q-$Aq4~*^zyy$U{;3CYHa<4Thm1BINp>r$3?XiwD8`&#&G*Sfr#vhJ#yb}Z% z-A#}dGLChOM&urh#F0GKkuX^Yrc?u2LO^XHjnToB>Q~{7%(0H&pl^i1=(trvno^A} z#ITC=v5s8dl%`aJABuVljE<|AG^HvG^<|n;6)gcqcNG|h>X64+du;VpU@oZNUgVUqArzh&Tx|S#p1WJ^5$5_tI>I(Io9zQ`pmJ8JpC**$2yLo z&m8NxmRvHAb^JYV5lyKQ*s`Wn;qe?(s{bS#i_r~atGmGHzKB{0Q>vn84Mw++oXoL~ z)nsHaI@YCUrc`;X8BMAFlsOGXcLBL(AM5xq3$_>?HTT*#rK)a#G^MKhpSXlIrApPK zj!dbpW>w6wj&IRtFuEMp!C-U~dEX31_Z`L=jBY#2%fRS5H>E1;IW_(y%QG0=D%LXt zqqC+|f6nsEv5u2jo;lX>SM(W-ZUpQ3i^st^C_aZv)VS#SSjT6OEEA(+9UW7u^g`>V zJEl}sQmRMxzXN{)?iZ}U;7bUIJ&eEWz)vjpL+9~X*N@K@9+tKNH4>&&pJdbRv5vwx zvPR*c@r10#c4{n1^wN0qlk=I^9_x5GSURRuhcd4{*6~6nwiq34Mu+xT$Imj>VsylI z98;>JSgt+R@p2YqF*?F-jw#g#nAjfcNbtZhr8<;}Ek?&D(>0}fBw0Hc-F!B}!RX#& zyo1pRUFSV9x?|WJ2cw$_Ho}yum;fwB_Zll_k99nR**d}KMxbJDaQ=jP7sbi* z=uV@*6O1mn013K_(b1IZ73?^R(Ou8-yTIs*P=t7FT~n%jR&kqN!ee^~cb4p>DOELb zbZkmB;0LSUD=I;~g9#-lZtx(8J^n@T`_oW+DfR%ZTL{I&G7&cylMYd%p*d`wLF~j< zQ?CMH`_7K)k`;S-9GFrZ{BXJV^*N3_QcZkx2)#qDfUhW7Oc7J8h~X}v=%&ndaK6t|j6s=8u`kV}T5 z0I}dDf`po(yh07#!_qR<2$ce1JK*2POZyKt6llyhSj`|Y_m@w@5kwJM9km-T$pWN*(bh@(7C)`^Q@55^9Iq2I-r_4HWPbIycW5H&K zR;VL01!UC~dbihTQdMV^^XBgtg}{BH0@&`E zi~219ino$*e4pS&V%)}V;((W#9P&a)8@SBI0Xs^^wR@e19GI>C0>!a2q-bFWWA0z|DA(kJijUbw5)Ub?dhs{D_;denfAW zj{LK6z#ZV{VlBFU**M@1r+$YIktJT}U@fX9CsrBOV2*%D7%wr|k|7mIOgSOdq!L`A15I$O~LSfK2m z{fz~_g_j)fi#}U1#7l<@R=J9Dj}PN13oxATM`~epjS2D_dqpX=?dZD11ScHTZA@@< z0V;v%ev%-gp(8GJ^QHn5EQC&;=WS~1qs~+Gz1mhR#NcJBRHqZ6?bDc2)NR7o;855C>BNR)h%W+EoQi=-5?N$Y5OARfX5V z4QW@ECo;m!s?zQI9LJxy&k{GBC9dTj6aQXFAX>MNO3=NekEH-PnJfAkQfu5Wr#Mb?s5wK6kQr;f@r&pi zF<1CtE444ZLborZ6~r8U1mi$>B6$R3Z?IEGFg}dJ2S#J!RH%+z+%Qradr_R)8QK?% z{}vgvFF(H6@d?~8>dq02&B!$}8Xx~X>Kqx}J|ahvv6b3~oQ-bZ)2zJF?c*78(NRUB z#fdoAYNN^0R~TPME=ITSEyfw$zAfaEsoOUM#l-yRgm{W|F-I`M<3;ueMw)SrEN)oI zx^;mYeg!3O^rJIn&l=p2NMCfetZWqjCK(yrkadZAq}%s56cUSS;#7o;3~opWG+Ly} zz#{K;@P{;V8QCKk30KUE#1V{GLB%3GLB%3jIC6k`3!FOMb9#bjTSW-M$%&wYcHAjJ3Lbw0sd|>h?tzH{`aB$m;g_^jh7% zi<#Hz_AO$p#SQsnM$Qq8%h)XrZg?3R;oyc7$j!kG52k-l+%T8Tad5*Lu*r{b1f!T* zEpB)yi?_OcRCtU!!40{B&^N*nj6`fBiyN+FK8qXD07ld?ZWsv*txC5q0&YlDGqOi8 zzE9>BH=IT0R<~~(6Iw;Z-cY6WlQBiEdv6-0<_Pu*D6zZ8Pcu zH#`k>$iNLTR1e{^irXY`Lv9QJ@58w!6uTY&oL@u^*h<|FzhA&@0hkIH*)JkkI_PN; zUW8zTbc`EDe?^W4!7O8(IPU48Hr@GXTB5s6%6mhL!L|hqg5hDiD z9zt*rc!YYBS#!GIc>=z0fjD_ zziiojo{YI1S6+oNL&}hkD=gWO+0gx}6v(@^THdXXOg)f?CsyxUCBeM|RoPBps&d*A zATCD|G;%MjSUR7ImR;K&Wq&PD!&|FIE`UPtq7^GI@%AguC?_~kAAJK4=pgc_$$bJm z;^ojD_NdoU`0jv&ARMs-yl{+rubbFoy~!ajAS(!$jXl<@a7}bNVg>q|arO97Cr3Md zISm2&nsNDf8S*WuosP@K9wRJPrqM{QT~bD4dnpc`fuGKUOK$@fu1<0S^kp{oI0-Le z<|jW=&94gAIu9*w?D5~=M;SmnT~;>sILoOY zvKW5Z*yBK~>!vg_P|dFa*G{*3bDjEihCNOJzu)ObDt_d-)!~gxF9{HqRQ!h(SA+c@cn^3y7z>4i}!Yhu8g^RGT@`Y9*K3*&BBk^ zBXv2o_cm;}xAniuXNF_8>i5h1V&1Bqrk? z;*)hfu^l>je0mab%v3jQhi=FF2rdJkOjBpE3_6QE?~xc~d1JE=eMB$4u8sCXiBqCk zc@eP4v^g8{rZ(y`dg-f@|L9C-5wP2_kX^z75HR1E@Q>c>Q6nNZ1c zBs_Rf(mM=qul~~VCleIppiGEBhYFHZWueAoGr%bX`vrP@=3}PLq6H&|s8bzLMQvHs z0V9W%_{Zf7I1=$RG&-E1H&1&cM2Rp3QJ%yhDDSwC2=ptNAf2DYijamV|HMI&Tg3mn z5C@2o>+66hX*Co&ji)iD7OXTxiQ^o(2P%{hCCx#1V188PipC>}hA3fv^cH3|5GC1L z^P?e8Nt5PB!#?D!AxhjuWqveTk0c7Bya$&wKl&Duc3^&#N^#oYXe@pz1VQb~kN<_) zdTf3)0gg03I*hpuM0prG&6*!QjBy5{sfhYr(U?9pzm@)&R?4J42jpSk=O3boekO5Iz^P}PbI|EVHGNpki zg<%K-QGSW7wGibzCRM79MY!$&qI?o9_;^8-b?6%}3Z74%%FeYA zEVjkW3&TNt|K!CqWm1WIS^%v@q0p) z+{faYAN@L#3G<_30zE%M%e)q%9L2;AM7f532cjIrdN~lKFy`z)lySy85al-JcOXh( zf7*d4f5F;x22m0>@2+Yi&5u6D0xd*&6zkJdh?3?<`K)U5qdqJ7Q9+c%Ghu#oDr6m> ziuV3^zX)xhqr3y1Mjlw@ml6c^&m6K4XE~;sz@Ia8Wz8zj7jJKz#prX!_QfV5AM28- z6X`7!DSeM1f8Z7Jf8St@(yHIyt+9VZ$0cI5n;#w@;&0j``h1#lY9_Jdkt22^5=s%q1m71CDXo_cE1 zDrbekmoiMCE~;4t+nqJE2`PZOie}(mCQz+eOrfA?txJj_f*X=)J{6db zL~k>W_7jKs34~Z3;YT*ovZK|nM75xWnbyW5rHN4k5h-JqJy#(}4s_h2hQ2 zlPmWG=avAuTQgq{kE{-&a>w%3wR|XzZBz{{FH@#YGu4c<&HqY_(`X%whKpLsL4=2= zrUuWYIZ_fGBMyT!=1;BF)lB%_)pQmnge9bHt&HNa6lP3ygT-V4@Lf*g7$(9@v7TrY zBrlbZmEqMop&)y}TIpU|D{T)abSHoPO320u*K`vnyiC7E6vca9xFhh77m?8?>nL3s zX>=ZDWV%gGfWDlTm5#m)SVsMsyV0)fG;- z5o=3hH8W7%&llj@t>0$wBd*dFPWTAdsvLg0e%Uyot3!z_9pZ$)MP#ZSehmn7>*wlF zc7_xF3;b@^jRX(YkH4*of(Un8H+2+$-Ax_Ezam|CaKcnKal&$pmqT@BLD`1(oEyGB za6+t@I?ng7tiE4$6b1H&GC6k;C&Uqr#xX8sd~!S!CzLP2kwQb!NTkYSx|A2BaQb0% z6UU(30~jGz@-ZK>9}egy%6>SYn<)EXva&;ZlA>oSTCM2*#t5~YQlXp3uTs2hi1C$> zV*Gwm(IQN+J;MID1)&GJi4%@GYEnwLuFnm8O1{@Jus@y~NX(BoU-nsno$4A|2rtq= zn#S@3s@AI`U@I5EmIA2=Y{ew1<~_g(P)xp3nT}uk+W9}jl}4(T;2Ou`oemPq3Ar=? z%bbe*CwzV^yg~;45G2Tnq6*OPr!wjWhUJD6Kw`NecU<_ra=y*LWux)m>w65joqyx~ zb3y1>761I)Q%Fihf^w+I+y`&$v-tBK#V_!_$^U(`G$gi)e)yFkZ-E$0*_+4BCh)X> zs#>MNDlcm9uG3JC(~j-O9&lk+it9J>Ha(21tML4_1Spd*5{ZvcCt*o%(h5Jy;KZDP1{G%`f|V@EoUp zoqZ2Bf*)<-YR60dwkoQ_X^nV__Be6h#xk|w)*m;?aiMri*?`cTBrn54 z(Q-GQy=q;EFFuYPI*vJh|MeF>`Nw{9$0Gga9g5FC`ED>cfu|O`#d9+RiOHMafP5Z# zemMOB`6&xcv;1g5wgv>k?Qa<< zh1&$G0Zd>d_HNC^WmsIqxMDB&DbR4F^hFr4@QsqbSnhPDJXHGfb1!AwVbYh#y^_As z(pQ)}%ZG1_^bO2?mT`wm-;i9s(?Ye$D&@uIAgTWn!u)(AX0ucL^Lx)GkqOFsv*Y|2 z1#y3F9;)v5xf<8d3xxnqlLPV3!i&vM2=hdbe5?fUy%Q0&Nn1JJR46<)OZzj+L~Puj znQoAX>z=Z_i@CCsA;?efo4(#(haowL-N`-_27Ng|#4Ohd=a~;#tWq%)Zl#cIuorn3D?iOAs zr{kuEMIE<}xONTu15qux;Uej@|;P(OGHsd&6M;M`*U%M^f0&u`A=fwAKm~E1=B*3-QnLuql;SmrvwB zkI_K^(b4#iIK$^`#-E?t>nsok=JEpha!v$^as#pYb1~;el{aF9Hy^*i`!xUejeNX- z(kMkmaYMucv**dK#{XzK<1-i6CNf=QoC-f)IP}>bP9~TpT+ohVdyg2kn^^H(n|osdo+Id1mE<4fySE-1ymxur|0 z#*Hr-KdE+Qjq;WvXaQD^(c|*J0#ilJtGb)!Tq&_da#);<_zESw`T0Fg=lkn4W5H8I% zhlne4Bp_H(w&Ht+14+wR*)pXoMQ@@LFP2TJux*Nc6i@X&7jN|W0MMWU~N^k?+zGfP_>+nNVt?=NFN4*Wv z9#EZ*PXMBfTMJKnGCtGmK4Idw*trN~e##^4XZAVU>hgOFD>X$3(@vMG{hGSrUCV54 zO<0~ga_P^}Yy}O?(;$OGj9~V?b=myfJ4Sw)srOvtqNSv`ImanKSL_(i?;O$#Ni}~If=#jN zXdEfObSb+WIi=gU{!Q{08QDeK9QJqM?9p^%vxl`(V*L3jo<-W&`eAT>%;_C1h4#wS zSk09Rw3yPQ`fP74CwT&wNB>}Ci=nB%JJuk?KNx>{HT369k{hld$qDC!U{O>Bej7eN z7DXYYPb9)WZe;jp5gfioUvBsyNGvyJI|Dh+{G?w5LdQb=^Ls5^?)eD{xPEyrY+;Az zRC?z1f|7{5oa5z+_!uL+-VvF3m*5w8mHgi~5*79r;{~5FPLx=sEVdQ;i^i2M5=(p# zu{*@-*(_l7<$Ytwrsd3iuWCLOC7Bx!L7MCaf3vah}rrve)504Sp%a zYp3}&Ai(7p13z6qEenzV&G<1-8b7+vf?qGq1b$xfw^dOP;jRqZm+8KZSVYyTWZn

Q>dP6leU^u3WJIStz4({jFOv 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 z%0D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܌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 z z10=%AAAYxt@upmX;bA*!Y~_Ah*vwY%5OVz2%4KtZ5PDs5z#aj>L?n4py{PV$C>kZPqL_f$_Wn5ABli=(U>}_pmc~g5jH=PFM2@ zuI-Fd8Tz&CrA~$}R~@DapEJrA71b9vP!?5W;4eDqfRA{y-So#M70Zd=;K~y$Afgf% z3O$dAPK5bScPj;(iKy`Gf4^`IiU!XC-2-G@A#%(jjuXrgRN=wATWii}@bGH^uxd!6QVZ@2^5*Dy!0=Zeqdzsicw4{zTBpLi5a+^jM)e(PuoU zm_P8M5L$(tztWtw%Y(l@M7R$Lt)wd|3#!9fm*0o767ZcUtZ#89b(m{dehc83)#@CxtV8@BfcZ{TJ=VoXux>rF z2+tlo1+tcf-)=a2vavRf#+lX|aO1_A*y3|=J=Vq>)Y=#$>0v7y=V0tULpQba+zLg~ zk7r;!qpFwh4kTFL)$Vb=)gStVz`FLA5wHHC@50ZM?E#n_038rI3G{x@NkS)sej0SL z(5ayJfld`V1bPqXkkDD6cZ1FndJO2DpvMUP3eLr{k#VfEZgsM?yxT4EQK=KI=TC69 zW>)M`dc_{yaeW`t@w82?=khB``Xazo*p2f9V*U!fXRzvN60Mk9 zNDa}HHwH^t>^*487o*-bP100c%o@5Js;PvSg``f`RIiu-sbQMx6QeF>Cu^#IOg>{W zTvLIVG*T&=N{Znq$KaOS#<1SXF;zwPW%vHV$ zt*-VRv@5fJ2bXlsaiS4w8G|U+ z$Sw=&H=^u*m<&Txe=^0rn3*%C7EYXf!Et*p9G8q@i9?3>G|DRPFNQSZ{yt<;8$s0b z;sbgQhfY}L%<9O#%(qZJtbUlJNa{PxNLbA}%}OH9PP#8+x(EjA=B+%{$CYYpD$$I4gpj(JIJPgjXK&a(ZX{%I-|Z zQ{a=QF?>c`TLihkl;2oZik-91$+NG%zXU0y#Z6V0mg&1}$eeJZ!{@+iuc>n))HD>C zQx)+XNmke^Gk{rA0kP5bl%3nbMqiUFK~XHPSzPyZ58A`qN|MW%QX^QkcV!rp#{Nr5 zk;s?*yq8YvJLK{4r{QiMdV-E}(BT&$m+%olNRdWzn{eB+5%NiKaks4A;@P^I&Ht}G zL=W#~9lz0~zc%VFon(WG4%9fqQt!q_gvMamwRAt zey!lgwIKO%8DaRTsRC%TSH^oJaiEQ&JBHVJZ;SHqSZ+p{a69j9^}&F}yl1#4+uN!J zzb~`5H4XeszMO1t>u2B>Fp&eAeD1urRRDfmyD@&8nwWffQ2a3NRKEp3exKMV^5e5x z(3n>s)5jRXW%m^HsLS=nY75+bnZ2#Y;O@)pZLP!}T>$axg8(=eU^%D z^CwR6=bnL9wvrp0ZAj!M+h?Smd9AXr9W`2pC+AjL*^1vh7j0~a|Bk({OI-o5+}U2u zT#$QRH+HiBVJG^DPV`Hl&0JCT$|3{%2_{qFy|VBy%jJyPIO{m=l8rNC*kSJ0d3Iy* zbOg|)V}CTJN+KHTW+DbUv$vFr^5vdW zh#0__C-fL1IwzJt?H33xBBJv`SMtSsZ+&I&4LF4Efg!;&1jh?b70eUlf{N}J2{s5W z7iM8xg-J z=648vx8RS({29TQ#r!qFb}=Uph96HvXO}2+Q0Oe7vx&(69HIHyLYB{kLaWaNg02>N znb>#OGX#G)QQ?4-$s(#_mpo$mFtJUkJRbTr1^&`h-WU8&nFR?fIDFZ{+Fz#Go@;w~x zH&$aWu^Ri0)!1*WcI`JhuKmV&|IPhIANKpW@9DGFwf_x!eM~RQ#3+{2$@co(ZjZt3 z@%wxockrB!J9ysC`+bqym+C9}Cwf`O^(}pFuXX9Ty%!s~wCO+b4qjYjdrt4*bu~M9 z@n#3FTf~Xl!JEp~qO!ybon`G%ddoT=Xsaea54t$m#d!kGXV7~F(*$qwI5X*Yh^D;G z_m!!pe3sXH5$LEA{6$ZMJ3A((6?zqhJ<_J0YCRr%p>GsnB>mb2UDVdps8$k%RxDSk zUPvN1lk{Z!aR5Crp4_1t42(l9x9q{K{I|Qd0T^sNHMSMa_T-J}b`FBJPk#Y~-eBq$ zq(L%!DQyrn74r>J1X%z3Um7z9& zP8#%A-m5Tlf&jZ zgCZ9S)TT$ry^#N%TOh}8dxUpH{%`o)(Fx!0aaU>18>l+(fo#KKhhsO_plDcXe!($o zsL&3O>TNu_{V7s+vCwwj19=Z-y|fecZ^8|MB7dXk?iE7-jol=dp4NL|!4_|n(Q^LD z^<`LL4SECw)4}Ng!(}>V<1v1qwfJ4no2U_G?6}5q^E(25^iO_7!;j+){bb^y+6sQ1 z(@XnH@DNR&?S#8H&_gw+<|PkG0N~;4g)v81gr%DFeAT{ z?SXi~;Qz4qCGb^M*WPEiAs224VYmp$a6^IuLUMtis8o?bnbd$xjv*llqykAv5VY1Z zsZ@Q6Q|t3tiB&6AY-#mbYpunhR;{+S&Q{--5c3}P`QD>dTRXh+{r_vPea^Wdfq-b+ z@ALd}^51I@YY%6ibM`%J*ry|pJ=k_0SK*BEOM{;sm;HSMnFD?)GSI{FyB&0t-vltp zLmb=J6DRvY{@=yPGMqO&=q!{{GkY+|^HD_`|MRTQeFN!&Nix_Litu0?Ftou@hcr@J7Jh^dUk9!AMszCcov2guUc+6S)KH2YtlGps8#N zXZZj;(fJ$PnKW1S4RIG6d-dsgTU)as2HhYkT14<@c-kM%~d27nq z+G=%rBro-UoW_$8_B0UEImayn}4`8@?JXR-{;{E^Df~p>`eQ1HFlQ`K)N0gF_P~V{^-ZE z*8bWKt!SbK^||~afFF>VhV*&*6(P&m`SeUWN9W){WOf;$Nlx!9vGD0*)P|B;A(}!n z{JxQI5O}{H5!45wU(iqxA9D|rOa-FB!RLppsCLqji2hMV0?ZJ_5da#^j}H0z4FaV3 zOffY4C~0SuJ8;NPK6L)ypJJRogSZ^R>_?6;O z^xhj}sF;5a(*_$V?!Sc+-Vj6O`0Gif43+2qFU#i}s*is!xePT_KiTEPD=^eRe=^ew z4VCggWDUa%Rp^UHxufJ;aKUi@dfZ6f(S{n~%NNh#h8pP~$8sYKb&M~YRg5&$DE~Qj z@fbsm_K#ybM;WTf7ahmZhAPH)7lyLNqarun#e6);{uZOMkK_L@qn21!Rsq8CtoRp^ ztxJWwgeebzYb?9#R}hZHhITs|RmM^aL3O*HDfvurWY3UBgmSof2$nCVRMs+NapT>e zM`S!!;GYJJ4Pu470>_fo(9a7MbF$wys!oL9Mk7Nv3_9(m1@sBb5WBy(yDHZpBO*Ts9_?3~gydGt19eFLH|jPl;bzgf8SalgtR&IVO8 zhIJf`_n*I1>i`>0sO$&HwwAvry-{ex3CMGv|IHp{b&h|J7VH5h^8Pk0*Q0EWB1%Dk3p>e{k1|B`lUes2e$|{+%ICeM`(-S6^!ZuKhci=BU3{f6x#flY4Ze7X!MUw zeG~nBw)2SxrQ9U{PS)_`Q$kJYQMMdho;F;j`d>k;|EwuD+kb+se$G&({@v`~3uExQ z_6p|t%Siq5HR;15CyPI5il`V+lP(!*GCD6fkpvDpO}^>hz?~ zM$YzrEM;dW|AoysQ!|$)X;$Nx%6kgeEV+c&Y?b##$-F4Zo6%qG@h0>Op7D}jW%5NH z?=QdLc`wUM$AZj`JIh@4Nqv z+Lomd|=(F?#Rp`xE>d4&J%` zP&YZ0BT(Qnj%-%4)rCrSNuI|>4Rc?S)EpPcuv+|&@Ai@S$P-tc1@R=sss7m>pWa?UVe$l)Jx51-da{{i&-HR-cuSL) zGV^)fRnn40NvtLu|9p>cE3aT#GMkw%P_iybXRCghl53OrIpp|@z1}jk*cFD~2>f!d zKwNBK{I7?ibwUCZ8o;sq}U?`99fKDgB_EOt4?oNt2E3wn?L1uwat4XE`)*bj0NoRYi*O84V^vDj2Jr52 zd-L|jq+wz##`pFB&R*9(V_uW^pWuJuzwnQ9OXtOrMOXp4Bhe)Hi!L8qJot;1+rc>Q z8Zvh04;q3dxNG>pa2E`E6#U&a@<^NK40h!kRZmw+vm}4ty5|r10vX>z#{FEE1mibL zTIy0p_jQGNDO#M5=pj2;;5)2nsA0y;{PF9apOX5A$UWnpm68Swais-w(E^U3JHph$ z0z0KpJ~z4Xhxk&9JM<(*R!A3&v}x!9wxTK;vlP?Ct&Ya@WuB!R7}u04;KJ9fq1nEP z=)=Yryv=skGoKMuCFUbTaGes)(4ic(hG~dZM$RqBj6JOR|L}OK(vKa7CIGRXsyN${H@NgIfy;Bg=&*1Rq@gMo zdcehaGz0;r$Nf+XgdySkv)_FS7zOYBVF!IhBzn%aYoa7a`jk z#0140-kV{v^Q!PzQ9<3}GVDd-?3ReOU&#JsvT4;;FE|ISDl08-ST2O1(Hl_%j+E~d9>>)V7$TU!Z8h=&Q z@|t>Fy?$NQ`!94sN2ajSivl*!aO$*Jv^)$R!|QV zfqfJ<+ivJovHU`_VN(ZNMeS<@lBovK`K_e(-2<+wiqEbC?f zDN03sy;EciN6ITIR#jaT^^;Pwvbr`(WABtI(0|t|7(?)W=9&b*UgV%0WTy$rfBQOb ztAEjuGWr)>AF(|1#c_^M8Vfx+KEQvw4q}AmHw8lmZx)B5xl4*cF z>^Oe`5z%_jL%pSlV?Q{Kw%+v!+qNR;q`|L&0X-}~%8#S`wnzA(>jA$T5RUS@3;do` zUId$VEy7WLe~F9>iUj<=87YIHvlI#sDF*bg{JsG?$}e`bS!1)my>Qz8{wu;Plg7#8 zO7NT9#f15vX*_o-0_1oW^3VyE0NQ$=MHq9m&L+92Fsf@{^P!qxE;BFpw{K6cg874M zi*OttT6Q3!Kg-}`KR7<@tGzc6bRI_HBMj(a>$SASX#BzJY2ZeP9DeZhFp<98Az=m| zt;7$D$dM&qQUCnf!8$H$9|rT&Tyv=T&&n8V0s?L4+&m z%Ofq^P8*(JW5gYNkC6jdZywc#cD-9 z?^$kx;ugi*6*<3|&gTp98O2u>-%Byo@8pA~&PPZ>X0ag^c-igk)&(}H|gD1DRSHx&6? zCZER?pI3ZSakt{16=S&e%->5fUoovXPw{-k3l%R`yi)N8ijOPGj_Kh0j?(Wd4vm@m z(u%Vc&rw{ac#+~J#TyjqcbV;dUGW1&&PB%ODGpW~t@wuGF2&y}aw4-_55>WXlwmM_ zqT(#Ya};Y7*D7AA_*KQb6(3Z5Qt?-c&5FNObg__QeLWTP6(=Z8SDd4`Sg}^|I>lQQ z?@@e6@m0ltSNu>h7B}_wRvfB0MsbqjnTi)Eu28&G@fyY36t^ors`$L(TZ-=~cE_TD z{r;RH{acg1T=9Cv_Z9z2_r#&tcTed!p0!>+^ofb!KY*CRdP?bI zh&ab9eF_ox&;q5G5V5{m&gTdE!GmLh+d&Y+E6DdIrz>r2-jR3tF6zX6FY!wAgD1Yy+z4H^joIs(QtAWC>cUw)wtaQh zLJc%Oy2DB=R8q@|Dx-A?bi8ooB0i+B)UnEFU9IU?w?H`zc@v@H8uiF>jBZ-@h&mZE zVGCXBUy7M}Sd+GY44t(=Ni7joQXAY>`OKNjn;#Pi(;U-2{PEKS%DogR*0uFsX356w z<;1>=v{+V*wyN>0taES+WmUMRGo=;7RBnZWnB)HtmHGwOGm_qw{9(fp z%!&E<`Q~`N-@yMEMV+zi?p%dXVW6KkBEj9cypgglDR6QjOz@5|{9_4f_gE^P_&n}; zOXVacMAGtdp+@D6ng*`WuYkYMCe#(55Ax>-NRRLY;^>v5RG~K}QGoV&V@*9y;zSl4 zXQ)_0mLw&HiYKaB&v--SB+yOAJJwKniO(_ZI79VGEOS9kFjT(;eNTDE8){%eOodM{ zRLaR4f$D#TGwym*66;uQDx+jfS#BCBs!=6=!Nk+p4XRNkwy?&ThJ+eb{CMPh-u4k{ zRO>)@2Zt@LH{Xox0!ESXsbsv+G$ED{uVQD*>Z%}~U|YO%I6$IC)gwl}ajJaWf>Q>N zYE(U9jOzYV{Hx&zdsT_QAna8$hIRZa>sYFF;5u+Z`LZOe9fx4|zDWNe&hxx)=0c6i zyGIM=Vv+A{({i~`qw>CGhDTnP@_sbi6(mY87eoIYE<6#+okfn#3`u3yWf=S zm-v9O4{~gvMwOUK>Y=H^C6%~`vEQ#1sxXlx{@^;Hh9~&)@gBKbs8P98qw;=aYDj}Z zn;$c6o&XAs{;{cVVuCVx?}_`Q+@!=GS;Ld;s#h>2mughr(}v5`#017ZYs$?|Y+}ym z3{{$-YKHg1a4wb#<|Sm&{maLt4~v{w9Rpdtjzf8u1$wi0y-D^aiShm%!m+OCfgA4; z8w8>kwGAVgN!rZ3pZc&wkJwE7@5K>tU%)^az7kTttC`L*30a}k&4`GzkHK>TGxBFq z8t-HFg^OC~RK1WCi}vHV9zg$`B$YkAiQX5byRqbvyhxUeC#i1{kU7aynE50vo0t4| z@;KSMiR&xqR9(i*r)X+Axq+D{d+aWCs&bfls-{jz-pkCVdOM`-#N?68HN!KHph-#o ztoCMV=Bddlrq0sT>B+NPkh8sqr0neEZ<+Z_&0Ly%k6kGBculBNbu%-s@>WUaMM>V7 z-fE9`r07&V!sLrQ-fW^%bqkqY>^+0*qEq!Gxn1T_@zAGE)hMQa*?U>ip;Of(#?j8| zM>f9uI&$}2jl6tV=XlVml1FRYWgLgQC-ZfcOGurnsU*9(X7uA=#(H|hitxYhqxkn{ zj8d)#ovIHxTm>%U$fl9y3YF}V{018}%za)`bDTH>Jmsn3Ik7j`iR>fZ$3px?4?0zY z$ZdhgR};DMwcst(5sQi1nYYMeZVx(DGnsm}$45DJs&ZNO93AcH$vTebxt=e>Tbg`< zna}gqOG_3d`N_^Z-{WXdr|MOfy+Fyja;0!6obmIdM)& zUIL~2@jlvvsoGzDfG+c1!XWma%T9j5W1UX?F8mK#0-Y+}J#KH_-k3B@>=EM~G=Q_$ zwa=KJOFVxZ#oxg{&MiAHs8tF_niVJ$i=pGBj!a?Y&7R9wP8#?b=($%9uruc}g#MShG)NUpntcnR$xf?r7HE3lhJREtH$ zvInEhA|DAOf=%qux4J55%)WXpfl7F~U2rZ1u|3^>8=2hKeKR!keqVf+|04 zRGX~&LZF(_8HEgcyLMU;qpji=)hjA-x5#%A)3}SJab`+UNvB=)i-=xQ*?|--IhnLVkzA2U?E_yI8k7IB$zq}ZM9py_S&6Yt>@?9Q@u))b~^8FSGd^6g^=A*)O zB-(if{h(<^ntWMAzZMbK%$ zeBl>8d)Rs{Z7~{u@VW<)fda$M_=v#tj2y09i;^HF+8XlXObU;~u^%SKoyTyTl!bH2 zX%CL$(KwmLFw?2?WDmyKC1MeT;pLQvN0zf?WtFx1nzF0fvdCLV*2V+TfHc?)0yFm8 z>#Z^Y$m2NAJ^a9BJ(L-6t`hnD^17=m88|{|DG!?0ndy@id9RZ`UvZh@CdI22Z&lo; z_2-=CGX{N)(()M#^fsj*QpC?*DfcTy zkr^W&jlIb4FNz6_4e0@jCn=t$C?>c_U!t_^rU3d*rSDVxf#S~<-&V}Q4JG^)k5!zi zC_jV2Z?V!V6=|Tua$+9|d_w7eQ+!YHPl|EeM$AtOPvS|6FDkyFxLffLid`tfMShx} z5epT^DNa_LqqtbHR*@EwEcZ`}G$$i{pW-8m&nmvIxLfff#W-F_EZ0x5Sn*`V>52;# zs}-+MyiW0U#rqXsP<&hQJ;gsOcH_q&)Z1TixFRjI7=Nnbe8oz|dc}>3H!9w#_<-V* ziZ3a)D8}N3e~IE0#Y+`8E4C_%tQYNhU+KMy-SE)n{hg~gl!$zzl^&xwLE{TJ?~z}g z3wSija{=@y-jAU9Twywfr+rx?SIVmbQDf@Bvc~j;lpF{4-4Vxg3`d5nk^9(UPoCyU zleV^c^pk;odVKiJp!^X&G+O9C!%3TcYq{qSn%<53{n-6$o%Btu4)*AA(w8ScQvVvC z$szHa2*jhf0Y5?C8IV6l_UwVr4C@JWM(>GSc5(jYhyE{&hYUx|HO*wee07|862?jz zm)G6NDa_y2u`Hjm5F@1Y`Ihx^x#n#nuRITrNXRP}G3603fV}cDhP|QL$FfXnHvVVd zh7>QK2~PZ4{JR-vR|X)jG^JweS$n+O6USNegx@<;Ro`{VQfPnubj!W40)v=$Sbp$GechKhw{o2RPhU(bo(IkN`ES&@S!Xoi77uE19|1`WO*+OQeK%$UiTTw$SeKr-;&Dn z5))ZjAg^@Uum?FdlviHIau0EAy@FKY4(9y+B|;S@xP0(`aH~+m6Fpe&ksU%Ad8Pj& zQ-hIL`j4458+oN4$SWz$^PhM~%1uhV&l;Y5S|}s0^q)3drY6L(L?EwR!<>P=?Ioesh*d*WOI-u`5x<_qPO zCz0&tn$f4clJ!7U0ss5{6#xDgqa;2c{=nfXa2ZE7jVxEFWS68o?}xdsNh;)(4Dghv z2IQ6hj%Gn#`By9g#Rmk%(C`7lR};Ct#6B(55i{~i_<&$;$}6Wa^=yxia>^@@WZ6(& zS>;n@l;kB)x*zW&@d3em6+R&T4TIR9O1$s^!8)Dzckn-G3FMW$d)(fQ%C!x!?JjR9cZv?h%=_^d zpT&`ArwRS&hfcB|Tw1ZeY}Z_zwhV&KKPRx7ibU8TWf)O@lrOS8`4KI@3WRML1ReTD zAH{$kcAPH&9p%S2HsvhjM=6u#SC4R%AB|lpd$xy7Yb&qJ13%k7t}86RD-pJ3$YTij zr5Mn|rfmcrZ6D>Iwtcxsv;5l1D^CVL`p~oo`SH9%(Z+uSxzhwoMAGg>7;~l0CZ&6I zPF{Hi@^waDxfb%uG#YdudF3|Dm!oVWBJGPrNsxWB$EP8$hA+i)3FsTua`gv?1i&Jro(_ z$t;Jtc&g&*if1b>Q{?@{eAg**K9jy%@d3pr6kkwmD}&sl@gFJn#-$*ie8pnLD#aCw zUsU{x;@1@KR(wG5aYeDS0N=Nj-lOOUy*Z$={*%wip7d2DW0x)p5ldymnhPQEcuAnTHrlOKcx5*MLG** zz7|FLs3h%U0uy^H7AR78o$)gj&r+;V{DR{3inl6$OYvdF*A;gw{z0(|Kk$HmQn65R zoZ@6fv0Ftxv0DX--6~N0ngXxYe77rpTk$c)pDX^G;(sglz)OeqpR9O>;+2XwD(+GI zi()ojHk1`66s?T$EYjdtp(ytg^6@)B$j1$I>ElJ67 zU>W06c$eVFkTLFojPcR*i_MUrM*OcBfoY_u<1+vE|Y9%ik?PS}gtb)*dJLyS#3YLoROY z?JjQgy{>1qxL<5+!MdOu@-=%WZbbg4q`Z4%V+-vn$s>jIM(hXhXzJx=C-27ATM%BC z+Ry?k)|QFs^@x9}InUYD+NW+)D?AXizPfjBx4$|sH{0^y-P28P>LYtXH_=_47f; zvSUWN>Q&;w-X)t~5PjP8LEid1gkb_oTMdX*S;WXvV?(nk(ut+cLR5AUgYC;wwFdt> zu(q<)SV3Yc7{&&%CVa3>(4j#rkfnOf$Qu(`s`yHe4Lbq(a$?>S$Yx}z@M(=KMwYr8 zEu}2=4yICL6HHpa#AD2PyrBjrrjW}ChDtek z6qS~s8j+&M(lwDIeo1NefasN3(l_t0gL?}yrkW?T`^+m@_WvMI>k)@KvcvIHlUy6UU z{TvQ7veYoM$Wk?PXJo0GbsbrWEHz+-+8PntDOu{V$SAT@RtZ@unNZ-vg=8QtjV1rW z)KHe%#F4RNPO=+QPtvk^$z#DIB1?UZsi$aaI@yP0C`+Bp%Rg09CnO7*8p=|sAP|wI zZehJMHS<)l;)}>qX;mMQrA}noGc|K*l4_0-S?YI~8p=|6XTqO;AWN-hA0o2Up&XBh zEOkD)MP#Y`q0^2m^-Z#k$WmiqBeGO^0EDts*^!~ENUUi4c@K;2N0wTI)`=|jMh;gf zOQn`gM3(v^rgkh#MSpRzBeK+YnSX)DcM|8oJtRX}>P=)G%2LnZh=j6K{vv}c)y$vi z$t#!|%2F4wY$!{mEI1-d<*(g{EcMqULs@D+vWm!3HQ$n|kcNVTve8n{6_O3^D@he3re>!8om5;V~JJFN)w2J8`lYO)8q z{QXnlC33(sdyxD(f=bmDOdC8!(&CA7u*)7ITAKyk66cdkN_01&;8RH|-|);8KML7H zkC1YCiREZf_6W)lxkRA0aQ4V)>})~5L_NzLW6}mDda;I4CM}itfH_B-w8F$Hwyemc z4Nv@nX~l*bm4KP1lbtqHI`MU;jbV?WL*m3M5nz&BW_Ztg1hhLDCETO%&nz+_wXVhv z28EI~Z_ogy9-WUwcj2HH35HlO7@3Asea9^w3~$Qrh_l(p>v@^aKxjxc3St)lk$t<) zPe6H2J^vpz%iEem70&%#`s}wqpGDLS`Z@7wcjm1(eHMqobH;YHl8Z)xExOjAW zqks18#}xZ{GnaiHsebNE#7!^ua@v)h<8yUz%)NdQ{$Q~IcdYMwU>XpWbvgVR1i0CM)3(W;XTiQb~E)9%U z+qg+?E9UFKel!%^*NqoZq1gYbtPr-r%PT9Y8X90`xn@a2Ej%HrU9h!a4Y~3sT~z2@ z-vAHOm5|}DrmK6S8(^Guv+S~3I3U63!mK#M$TTwGuuQJKuttoi)nEO}r74kXM(kBk zOMPAWs;Y5xl3orgRcr1E(_j#xMf8%38pP#3losHTQN*R0md=?T@=bfIA+29pQ%RXB zyG9e}N_?CSC9F%!SF9+PG=y+fVZ;f0byU1^Mfq|B871u-D^mYVYhCM7eaZ6LdUO*l zTwb-ZJ;&i73g<|%ZQ)ZRToi=6u+_PpZm{vBASGl}080j$#t%NJ>3&YVCEev}pN-#$_dZ25d zHDQKN=9TnO%y!G_WzzC`s`v9U8I`D%U5#bIMx2jxO5yt18zl6W-qsp7a0AbMHFTEJ zJ~uFLR*QVQmjz`bX8gt4y{sAqe$u7r()0n3G zoW>DL{_2(T1cv5EC1#d&Q?RtE0W+?O%rP}ruUdsBmMuYyal3ES8pPlk*QSQP4seNO zKLWW~F)Fxz63Ip^KenXYYN5|$V98DujIA|nW{u!iF=WkZ53Jb z;bMkig&78!N^3yeI@X;Y@0kQ$DM>xk62qLQML)NpucuPU#v8j%`41$@rJ z(`ZT6s*6*n%so9dHk}^-g>j?DjBrlj3!%KKyrKq5b_ix6`M7ygPoLp*@#3yiRDbb` zhVlv^hEs5v4bcI71Bd$mJ^g^l>kcn;Ioj$6`~?wdr0{z@vojwSlKbihM0g=t@)D21 z$^2Lx$g#PDe5@my#C%spB5ZJSn|!^%m~VJ{*nDmE14iL}zXy$HKPXGE{g{ieEkhp7 zkVWtdl09s&t$x73eA6%TWB)9_3WRML^4Q+Pj32{1n2$$W{eVL7%R@Z*u{)L@f1XGA zy#;>A8XPvQt$qOQFxW2g<6f)@P~4(;yW)1mM--n?d{yxs#osA%k-&E3Dh^Z} zsYtKHOy^#K#Lp{oZ%@y2oYjh(HU4JB#}(J{xrcms6n0V%V>y1O25J}ww)IYrAKO_y zjAeK}h~Jj0I)_%LcQQ#y46-x@DYRMsq;)1hTU8nUS&;IFG&?0L_6?tnEE@9!pU6?1YB0cQm5d5 z_7BV$w1BCul z2hO9}7XP4}M{|Ks1%m%&$-sH^R@Sl93_%Qng{zRuKZ038=g~CS4ipGzZVEBtevGl{ z%+wE@NAtPrXE=}c1Lx7>Sy^C={ULJ(&Z9Sy3YwS=r?$ge)%|Zcm*)V zK97L|JCE*$9{hbdkETk4C=h&w-3yJet2h$YTXZaW4pS`|PaeZ7Vac52G0YqqV~azA z(0OzNGl$05l-ogppc2rIF?J&}ht8vWGIQuWdIhgYXpDU(Q$y#`r}CPF&ZFO9=Fk}X zzu1M)c{IhGP$00*qj_gSpuUco3ZC(j)J%o~!SO;r?sjYqp6q{F~&ZQBc{~wkMok!oyM(xLW zG_|a(^Jqw>lY_}EbRPXYi-*pme@f<|^JspHf&u{_tu)3KPYIzhwy11{&ZBoSbLc!8 z?_w$tJOrRI_VcVabRIpP*CKQt%^%%{^b&rx z_n*#AenQTp{c1GWdxcAgBZpzVvxO~-$(JO0L zR*mN8@?!bQUVQkZ*Z86%KNYOV*=+reaqB?lOevg^E(apzT(@|}(xqM;l3^@SGi<8) zd&M3cec5+?UT&PtvX zydgCum6Ys+qE0K~j0yomvCBWW^XJfb`j7>YoU*}u$`oJ+&ZDE>0y6}tZAGRLd6lpd zL1G}2HSNV}! z8qa=E z?qU1UR#w^#!l<#93G&%sTUqIO7$}Yd`4KI@ibxp*9Xk0N&VU}w$D^&Rv=aR6IOEe( zz>nA1wt?j;!H?I{9yYBO5s_hV8Y1oEdcwA^t*mq-5>u8L(DK`eaFpMrk@oFRR(dD+ zJzzVj{K)f;2yXA64bo@t}sjl(Y5g&j5hH9L)s&Gg-jwZ^T9^oCtMoOvyIlNxXA3%|_S zbx&^0t82W`a~9H{a{=tF$K7-CjmK`B;#4)o@@{VJ0h{dD;wxSR{i2`uc#E62xXDegZ;7R^ zXm#_hXo{t;Z-u=z==H6!)XlAK%G>3nHZ;dlm$$~zF4ozlRArfM+Vtk$y>DjcT@HSk z<+FqGVgG5%&2e}U_?Zm!5d2x|X1|b(Qmj5Oa7>*Q-9C2^Os^>rMK0cG*4A3dh^;kV z+M%$NS)DBW!##?_m|fTD+GG)8-1&UtsHl{8PSz{qn2|EIShhbC3o+e!_wy(P;c0gO zgs1%84YOU+{@EbB-tVx%W2C|2!ny@b^! zKx(>b(e0pISYHKh+;W$q7cVfoK8jt5NKHke(u-XP&93i6$zaP};|^M+ zrX6g#D~4Nv+4a}RE5q#C3tU(~$I1h#={amz;KG`+Z!a*rmW@#Zvulq$1GDR)yrvmu z*ItGTYcIovb-3m37}giKu>J*kW|&=jfeY)Sm=;J)|C!|isp)jq9=Nb>A{Cfj$7nm= z2^ZEhR?IZJ&d10Y8*MSWCJB*tXUwiO>pHOf7%r^eLBkzxxjTSe52dCQpkq^A>%#i$ zQ8J$VHAlv#<|H3x??b8S6tKjWyViyEN|p_!rn37yg6@7L@)b|gl~?b7!o{6^`HI`iDtSdt$a z+<%Yp`=1Asd~UJf{)dvWBdg)XdUuQkphR_K#f6Uhu2MOuiYCfgd{DXXN$fxuTK&8V0rnABV50GkzZdl z)9*g>*pX+{Y$_f-s$|os^z1X%rTuY5%lxi0XQlml)mz7o{5&j)XKma#YxJ?pW*5)e z=->b44S4OlsGE<4=pMr<^45bBW@_%hYu&57&Hk0KE3z){vMGCZZi?Smr43G3EZR#k z56U7R=UBKsQLG0N!5-dG*$ezdd zsLivWY{|0Ku<;f7dj}T8q0z8NIakOoxE+`e2lms(j@DQRi)FPLWST!mOoPihv&{rmCleOJg}n;T0uWA51Umv+F1%j6fz7INW%b$X@S82ElBus%eAb$jya=o<%X z5jh~1%#c$q$0~_(MSdH`>n@Oc4pUm*xCr0H#K>XEw({zZ@=@ky%a9H8-5iOq!O3m% zQ9GV*S$o)gZROQ>p&yIUN%muZ^6JU>9=_Z%L0%hdE3b}WK(V4cpuBnqguDDAXAe8h zZROQD;79o$+sE!OANkkgv~57pc?bN4GN6Y|Yb&oF7-=8ZFqU6id36_j>`qx`K+A6< z!dSQjM`5IWXhOh`wudZZWu`}gUw{1WvIo<6-l1sYPew;OBd=bAe4UY3-;8{nkyr2R zXY4g8W~1P9Y8&(?)SVL6`p;(mcnPiiiL$A^rCb7;X^ zW`q0*fI_-LSiREF^c?~zxU(4SDfeDWgnig1A};!NVsCuuMIQ!Y-Yiz+FDvHf^TER$ zmQMoNrXPWP3=4ZqAqOsp9Pu9n9HDfHqR7V)f2z`_E6!6~rpSAne6CaEy+rzM#Rn9h zP<%o0O~qElJ&GSG_C`O*hpOB}@jDKzQd<0ugZ`q@;&&YM*Ob0nk@h+)|G1*~9S8lk z(t8yDs3?BNkuHA6f#P=@I0IuwKH_&AxLE0xisE-1@!wWj{EmYbzvDphI}Y5f>Ed@B zwD=tdir;ad_#Fp|9WU@g+|aC7{Eh>~?>JEWjssuNbn!b5TKtX!v+%HFIq^FV9ICYV z9S1Fb$ARx?y!agl%{jwz;&&XFQd<0ugBHKzK=C^c6u;v@@jDI_zvDphI}Q}T<3RB{ z4ivxRK=C^c6u;xZ7OhA8j)NAz<3O?F1&ZHsp!gjJir;bIS(xZ-ulOAYir;ad_#Fp| z-*KS$9S4fvaiI7e2a4Zu;2)Ha_#Fo=e#e30cN{2w$ARK^94LOrf#P=@D1OI*;&&V< ze#e30cN{2w$ARK^94LOrf#P=@D1OI*;&&V0g4ivxRz(3;!%l?ae z9M~H#0n*bH=PHVwF6LeZX<&`UuTi{Q@oB|hDK;s-r}!tu0^Tp+gGZ0p5X9%>^7cIVFJFiKk>l9wIF`Dx z)lWUq(!-hC0@?UWuG9EreCMWJmo+uE z*duv>+jPOM_|9u~{ci8xb4KOC9v6B2bVXAt1(~+9zI7mE+i~~{kEL&r@`a%D)F-+$ z3;#+_8U3!axUqRS$nr zi13U6J&9-t&};FcQ0wCcl!5^L2ml1=WBCS%3eaJ8Ov^a+TRxP*4g~0ib&T&MD?o?Y z@$q07njN1)Diolr9KVjw{0OFT3?K9yWD1?US{EWMFn25ukUl2lSU% zK_Ec?EqP@K(8E2TpJU~L0KJms0s;DZUX4J29wQY9(79|1{gzWAdN6*=^HHl;Ffnl= z>k9AQF*|;V6@>!yDzXX%=xLS>&5r+tWkUfvHEdwZY4?EUof-98ei>Ou z1n5IJ9uWaLKlDWXmd_!*uK@i;vW*DPDZ~@AV|f6C0`&9Qnoxj#GmGsV5?1JCNN9&}nweUnMX*9tZ9elHNix6rjs)^q~NK4o4&up#P2o z5DL)wgCyd&d?p(f3eZK_D^q|T@mr2>15v-_Ib;_0(5@Yg4yxogaYgV&EL}d^IIOf2N^mmK&RR9XkN`wfX?5#2l89aOOPQz zr`fSs_p1P%KL`)eZ+Sj=?_YpE3?l>q`mOlUu^9CXcVF_7B3OReV`0vX-=I(KD@w-1 zVBLLH$#{|uDcskT%t_J-t27Huo!XMs@2Zzu}++1ac&+3xpZjLkZerTcYZ zc;Eso0Nwu}_xbq9Yuvg)-VbM=3%avc?gfJ(&|ld717scq5&oj?yAg8d3>m@Zp5LA8 zLU-i;*h`qpoO=xrd>$0f8H02q=$ z0{~%u?rUTA{qg|!*o}AkU90^yMSj;J^S{`?2)@EM6wew}T;ex5zpFWBOmT5Mf7biZ zti{;kCx3>FpTEoP!K*nnH-4?V$rGtP;~96U#OGb>ZiX}QO|j{@6C6jr`!R#_7%nY8 z#)h9J>%_aR{Uz^kF3M+1{;ub@N*wJe1=t@cOy7@EfDxDeeJcXkzSBM_*gd=*C*&QD z*+IJ`|J$httSrB%yk@x=81KjHIQ*Ee#5eCyOMtS}Pfa&q1w1;4gx8p>8yDk$D;)uR z+^VE6YwOnlcTT$=%lAQ)8B_~fsSGMMP(SM3^(J;%q9ES?!`Zjy1|82Rdq{h z={`L0d7(F7;60&}4#$IQW_n4@19os3q)?%U@y`?x4ov}paznh-<=Zx2W+gbIFE=cG zX9Z>&ZIryE7eD<())gmIw@10cGKCZ89`}jM} zmO;?D5ByRL=wZ_~f{yZgFNOJnIQDmc`UgJ)zss>+w+H$0yhG8()6Nh}w&1Y!-isJq z(mI>48TkfV0%-F=Ow4@2@lE9GjQ+uk*erV;m>@gk=Vn~kFZMOcuN3p;i-@y_t=G~P zqw#+W{R3W$YiwgMI@C|NDMB+nCun34kQ|?cz5=fc54M5&2_uCv|2WS4tYr_* z*YnIz8+Lmz{X$CsNoVRS6mka*eLt>lv&0V)%!^ZV?Xy6L{t5_+w5!)3Qfg2SR?goruZAZzKa@+{*E0y2uf6FYx~B&>uPO7tvz?j$l4uiQuDmC~Ze0Q!qciyi}L z(PIGKt?{DA0QzyIMUMfr=rI8IXuRk#fEGOlpy)9GMUMd}dJI6(V*rXC15orBfTG6$ z6g>u@=rI6Aj{zup3_#Ii0E!+1Q1lppqQ?LfJqDoYF#tu60VsM5K+$6WiXH<{^caAm z#{d*P2B7FM07Z`hD0&P)(PIFL9s^MH7=WV302Dn2py)9GMUMd}dJI6(V*rXC15orB zfTG6$6g>u@=rI6Aj{zup3_#Ii0R9AXlh;-B7=WV302Dn2py)9GMUMd}dJI6(V*rXC z15orBfTG6$6g>u@=rI6Aj{zup3_#Ii0E!+1Q1lppqQ?LfJqDoYF#tu60VsM5K+$6W ziXH<{^caAm#{d*P2B7FM07Z`hD0&P)(PIFL9s^MH7=WV302Dn2py)9GMUMd}dJI6( zV*rXC15orBfTG6$6g>vuP`p$)zGD<8DT@Db#EV`7aD~QSs(7{HR>f_K-&cHB@k7Nf z_?W?Zx)brWrl;Zw8b4j}JjG?2zFe_hAvuFAO#2z3Vdl8(44 z{{bphSLGGt+>WdAuQH2qRZc(T)EPJt*nz8Z@h%(a44j8`eW)|Q7nr3w@2b2ErKmGd zkM@N+11Dh6LRaN{wT3zam8@rcK6>h5Thn~D;aEfEB~E2ppfgZSE`iQ~%bbDH{SKy` zV9KSOJPM{?#wj`jmoY7HRX&U5LRaN)FfnvhPFL>-a#fxH2hkajkqumx({wh}87L>; zz*RZha%ir~v)G`(Rk>_j95K4T3d81$>^F2(em~j~=nNd#Rr#%KcHpY~1va}=uFA!S zdf=-3CU!7zRelGX6}T$rk|lIiE_xt`;Hvx%atU0Ozt0*va8*8%IRl-6%a}9J8Gz1P z#8vrSq<;B0avY4S@?N+ke_yW3H=)O(GjJ;}Luhm_+7s3pc`PY=1Xwbj6i3^Z%t?;p zu!TAUO&qt-Rr#fyhoR0u12cE%s(c$Whpx&+g&=fQ-ayWw(fvZQ3SE^`I2LhL{-4Yo z>I{6y4u-DEw=#3+s+@Ob)K&RYOpdrJzk$pmuF7QxfdjiL??pBdSLHjvMsx<`0T8+> zPcUDoGccXxeq5FR5u+qJ1MhRVLRaPYvRvq@{5CdvKd#DmVMwB`%8w$q&{g?MEFQWl zzk$ph#V!W^G*iav%5^PR3+Tva^lO8kRb`u_cx zbL8R2yp*D9{$Aq2xM=)P(3rtEX5*TPe{(Uu5r=S;dr4Y*VraYAWrnD*>=So$Zhpd9 zsbCq5ef}}MotKNG)k@D~tj(^(!Oqd4@v;#JGp}p_{Ey_Z{4j`*al?dL1w$S#dkk|h z>Z_2#fSC5Tk;2NB&}tj4`)r5f7UZRAcLwp9yK~)=I&<^-wtLB&O{D|R61)h;INr+7 zWyeh2l0b#T6t7b}EG{a--LW&bm9MO?En9}&o0r3lg{XpX4}++<)moR^&*pfdmNT=c zc5st=s8@u$)T;{z*@0d?Onm0(T-+cOVJquHa&+FF+&20)z{*4ph^sQ>b$@{zB851< zi}1D&*N zhp&6+B>Tbdi|j8}h0a`@whV&K`4D^^7m2XJwsPC4c)hYb`4KI@3WRML1f8359A^>( zdf0JpE4Q5uektT3KYTU~_)#Vk<@Yl99fdf1*tA+iM25jx9%&zn1pL~{ZU2PClw}6A z{J2t%@@oJ;evf7Q_9wR;(A{zV)pk<(k>?#62FURX2>2#g0%+^K7h%k)I-8V@@3Wne z+YUp%&d6;`A!p-vyaUN?cVgb3U>gx>UnEL`w%sH?4Y@7nMtB%M=fi^tSLV*B2#w1j z`wp9^XlpsnncNPCojKmRtz7lLG(L{YNdA2l3l+r|CgR1PAdvH%`S=l( zc#Yz%irW+)QsgHS=KGamtKxqv{zWl?v0=UeiYF5CQly?`{oQxP9j6z`Cr|4r%l6zR{F`Qms^5sy$5 zABCV_RQe6Y-HNokVg4>E?@THdDvnc}tVp8_=BL^|u~zXi#eY(~P4Pa(M--n`d|h$3 z;zx>cOkncur&z3bvf^~bg^JaR;)oOFu2cGU#rqXsP<&hQJ;gsOcEi2GdiyI5S3FMf zRK@v;y)ebZ&dnD#Rn9hRD4OXMKKmP^_D13QM^=fvtp~F_)kYYe39{f&hVd( z_`!?^ayK`ok5H7|Y!QEwrVr)3N4h*0@UW2Q0%$(7XlI@#g2wPT9{yS6^N#W6S!J!h zQwQ8Hc}&TKv@!fXu>A6)LR5mTl?V(++meo(Zfs7#C;FZf(!Ce5!9w;_-;@a>mB`vL5qW@SYqwsqfa-yUQD!uV`N1lr!n} z)?8;^^9^Dvep9Qz^U0Q+)F${hzX8L(X_q@`%WnE9k2}}G4tzsvPSZNX8-7d|f9hu% zo9w#_cQk{qdm8F|YuMJ-E8Gp>zoiwI`!_MztPAhFD)?oe)3O_W+3B~vW$)g5b0*!? zil=zupZ6+FG1 z^ID)MbHesMJDRtDp^@KSy2Vi6sBw+gPpI2?1Fq$*eL$aExBdpaQ*WKev|}5uUx_qK zlUwKanA5WTxW?<(fX3~4>->`%;p=@PK1I%H9(8=<^_L?4vbx5NACzouy|Q^@Q{3S= zmNw%V4x9TeYHxp5Gp5U}ac7I9(dIs1G9wu7GEV{XgcYzydZq=UB1Z63z9ApWw&jT`TPkNC^f4(KKa@?8n{GtDv#DANm%{ zyAiLAxy=I{=PJ+XN#FhhHl(mmclzlT(y_%Gcb%?rhQ4CgpU{qY)23Z^ZY@KcF@t{YA_@0$OJ$F6xdr}3KBYUI1E0CS-623*fuTm2nRwj}i0 zWAA_`aX-A2<(%7$>pl)zCM^ZHwhiMT-$z^Pi13Zz=E1+dv#KR;@%mkRPr9O4kMzb~ zdsjZ$yT{_EdvpGFfgS+;4!BNTOJWP|uez(6oJklr@XE$@#5;dvzusHX_j8)hL0k^P zag3?;yWs89p1DDL9_0KV$~k-k_6WEf^JY`)^JBNRX5qea^RS0N-Uj$p*w{J>{d}+& z?i1XHUFsT}x&L`b_Y2m{a=(mroPF*W-|36{bKS20!8rTQ5iRkiGj_Rkr|tR@+8s}G zUY^yAxz}wv>f*eR>xXN4_ujo%;TKcOok;VX-p#*8d8RRpy$|F%x{FWU)gSF-KX~nO zn&=OLbLT)zk+xnj!m*~0SXxniFwoR{!L@tYm_ zxQ2NCWaIwtlHSz=S=mtS{fQ#P)@ZQrCZyv}tkXeXf2=lmHc zbyYTHI~$u)NXOm`m`~?4JGgG~N$Zh+T??Mk-Fi6ZVlFq{h%vc&HSWzA?x|Sn*4CW# ztXo4D7eR3#$KE`Gf;(m&IHI}-*wcz9{&Hb9!*wV9LL-R85h@pMi$afy{@mhQ7C-E#; z-}+_5#dI8a&)kP=JpuWx1X;bSPzlrvrJG>bVHWbFrSw zfIRE;Tnps6PS4Fio?G<11<12e&uu`SJM?@Q$aANj&j5M8sORfIo=tl02J+ma=SM)E z4%Qqzxz^{Ir)NJP&y=3SfjrZCP5|>&qg56ZF=qi@_bm&oj{(?==ma$=j(bl0eSA$a}SW`M|#Rsz;uY~Nrx0X`{|hi z@*J*b8pxB5Gk8wYb2^Y`sh*2~JQwS^49K%i&$U3F>-5|VZXnM+dVU1t>EJgC&$ym>K%V{dOaXZg*E0>|IYG}!K%Ud} zECuqU@)FO*dM*RA4%obB~@M0eOC=D|ZOGa{KIE6Z-0js?N2udrf3B8+z=Cc=JgOa>w+hIvu{8_tIwytCs7Hk^eH`a#5Xu#kHmm}_xK!NL0d!NxiN12Wt=haUW;~Y_C&(PruH_qupE`bi;0t`mDagKbV z2zB^M(2TQDjclBgVk@RHifsYmc;Uu5tH?CmIOkfn?Erp%_&Ab{b7W)#zdzKk^A?(J z#S(lsg&XIvE#5(GoU<6*xN*)@mdx<`v(LskgV}{}mJ4+F>R5Z=_lH_+k&Sa^vlCgpCIeg+H+$ib>`fAWC8i@B z8xQ_}Uw(i1E7IY{IYsPxsKeKlqZIo6`7={38Bc!5k+Eb>@*7|h**NDou#9Y+b3e<5 zI(*xiIrRHOk7Qox_eZuh3jO}1m>T;1`4`q3>hLwOqR{Wp2h1G${SjN6P>1hsmJR*> z6ftw?_lI|8bmN?_k#)rHj~I?b{QlIETV&&$d8GIC`%}-(M*RNJ9z{0Jkq1EBUB_XD z8KzdznkH+eS4HuU>*JbMxP{kf5;`|05F&mu3=BG}+1mcXnX` z8#RyNyE3AV<$SrXi>wi};1|j3+ZnkW^7?K@6kUY64;nX4rl2EoP9ueN|D4+$4MWqA z3x6L+fzCO!!qClHzPhrixc=h$jyN2GH>f~(CHk+Tt_4p`nD2rACWO_06GDNrC%Gnk z9pQJ*4ZU2yPm#a82>)yRnhk#cB7bR-KcEQvIGs^Ca%}O~x&GjpGiHr09_>Hq7R?&t zKYUsB(%G|TrAz(QGyP5fAP%m{>4SdZmRKD78U!sMPsms=_6a; zo;7RM4F79io;jm zA<>A2RW&Qh8kVi9!k$3OE1j_!X|;7_b>)@Lv}j~K9t~!vrbW@{73FKom{e8C-Ljku z+om**=Ag2Z8qb&sAfq+%?dmgRxVA+HcW?qPp*A@ZhfqsxhnVsYpYUx z5c_leEqrh-sa>_I3gb)#U8>vSrm9$74PRLG9Q-On*qfg>Wq9Kq1}|N__eCc-822)6 z2)EWz-(2+#<*ORFy4s&3ukcpNeD-wc;H$2DX;u9tQPTz01FKrwP_?oWUBrG<)c;yu zLr+qj+A|CfyE2q@RFB_|*gnrAvsGHVyNa3lZZ(xpoiTfwxQuInT6Gzkp}NZl>kTYW zapQx==wD;ExT^A%tI6Gz8c!r zM!h?*NGPkWtF1>dj)06DeAcb3b<9p`fzG!bkc0IbX3MpCAC#3>(%H)V*?2fOWlO6X z#G#^i%dVNHTXr?{TQL08(*`H5r4wjm(x7ppS9d~1) zxq@wa_=YR{f5rFMs;cEh;wZ4FqQ2fKs;sJ5y|k>nq5@k>Iz^HwG4ckE(0Ca3oGAW0wJScJTD$CH#6`D#=|jdINCvnHu);&VS~dEfgdEn@d5ZzP)&X;%Y5WltZ4at zgcTXvXb;PeacqY@n6H;v$VrqXwz>|xXD5fSD0Xrz4@qJZDk2-`B`@mKImF`$P{yApJi-&2wH^+1~K zFLils8L}7*W#JS9df2pWpczhU{7K07RXb@ljp+|)ShG1V7i5l~<@YEuU`}xg%aK#q zIX(H2$oIT01ho8!k<0EpJlx3Rk3c+xd(vNCAC8Hg%LqE#M#BFP1A16~mbMs;KX^TP zOx(!f2Y0)P^kqhe8GN*ob0|Er+R(n(FofyG-`Op}^@z5X{mta&JDgGB;mZzmQQCSh zGjTpN;iHEyBO1o|&@8njFzg6tiQNptoM)tYKaYyg&@q$4r2EbaP@G#f|79As>0bpM z=ZT1(=+T`B^Wd*3>gnHaNcqp}{KGc%&)U#0wV@;5381A9UHxEfXW#0<54#{Gb5WWR z9r*zn#7Ex7_62Q!f(3cn{D=+WW$_X$sDfx&_6L;RcTOsTWq~y$n2Np*#B=N>BAyqE za03j){mXBWr0F+{H01~bp?G%<5%HW)UVq2AO|g-P8|p419(3D?xVg3yp=i5<*vC8v zSq?!d-%k-Ng-$6JDsrAMKj#OL^MJ^A3X#uW;zY$sitHETrz_4@ELEJRxJdCl#l?!1 zipvz2E7mDCD6Un!RB@f+CdJK)*C=jLyh-sE#oH7c74K5qrnp^khvI{Z4=X;ZxKr^d z#b*?sSA0?NWyRMO-%@N+d`EHj|7Gu5;H;|3{eI2NnK{ES0!I-+4-6*gFmr$r1~o%K z1w!$WzDJXF$~DEP?K(6o$vjHO9NWl5%Hic00PG_}mUHN48qn^cxocDeum zx4*UbI){0j8PKfD`pudD`qpc&z4qFBug8CXpW>T}I~Ctnd{^;3MLu-d?&L8L6N;km z4|2K6JrsK?_Etr{L9tG8vf@<5X^Jxx=P1rsT&O6U^f>Nv zl`mAhSaFr&8pW#=uTfm9c%$OYink~>Dc-5LUU7rsy^0$ZA5eT)ag*X^#U~Y?ReWCY zCB;`1UsG&W+^+b#;+u*)72j5TSMfbX4xr@vD<%}>yC<|;xyn5hdn)!;Oe=ErljV@H zB-dXtqgbO@t9Xp!NX0RVV-+VTa`TVnO;+Ulj`B3c8H#fh=PNE$Y*bvXc%hsr;Ct=zB%D=z9gqa~AkFjTe2dkVW4sQ1rb5g@XbV zeXqb7ybpnk6)#r&hN9?uMZ7#8fTHgeDEB##yWz}#sG{h5g)I7BfuipfDEeN3D>a?y zdxb0{IiTo!1&Y2`;5(X5^u0o+@-!Z&pQ7k{g)I7Bf!j6w&x)e&72%@q6`01jl=-U_ zMc*srlT{XduaHIGD^T>k0!7~|Pz*99|NGy;=-z!k`y#htwD^T>k0w2)yuPAbDhUJUCSD@&71&Y2`py+!A z4#hphbfWJSDEeN3Gc;WEy+Zz^%A)TT@@*=MzE{YPs4V(kA^%Ec(f0~j^t}Q_-z!k` zy#htwD^T>k0!7~|Q1rb5<-0_n=z9f zK+*RK6n(Ei(f0}zeXl^#_X-q!ufT3tYvS`|f5kpTgdd?eLh(e!^As;pyjJmxieFXS zNkqACEBcs5^LxuFM3g^6akb)S6u+o=x8i+@k19T+_;bZ=if<`)!Gn|KA4J4+HLY?L z5f9IyDj!G0eC!03ClfI*J45ApM9j+;s{9Ee9>SNXd@T|4x7PIAqI z*#RD2Q?)Iq7&<_= z6f%=tkNdX(Mr-k!{)j+IkA)hunEkDT;5m6fR4oWSqw|4qV4M%%1%{=gFI)O;vkd1N zJ-BLom$rDb72cl2r-!tAfcGS*&*FES(}6A-?uIwd=}J%(WbhUW$@2c03@PyciIgDA zzkmpk-}Pn zp_bZ4lt!}rVaypRyzRxx=P0}lWGubpC`5JPrx%|>&WJ3}qU5qKb-eX`nY$t=tG~eU zz8VUNMN-U(fK9he+Y=D4u+z@b)?8h!oz+T;Rda zu%RmkBomCwQh4iqKRiOFtRCCA-Xme1YayZ1kA}xPCMhH5Na5|*SX!3CTknTqDPxi{ zXLu^iHz6q_>4+@PPgK0;25^*AF**4dr57HPW|<0Io;-oz zS;Y86*Psuu+nnsg6iwoD;P1g4U4!xX-;FKcvT4F{#mB=Seg)(2VtjO5gp!S%p!LwJ zkw`iTdgL>VoOuE@$6%Tw=!dj z72Zx}-k2=Eo{3|Hw+B&-$?^|Sj1}I>BqCOLOY*5jmj5a04zhgsfEt^kzE+Us?@s_K zCZxLZ62)ZsOPM!Tcq_{nvBF#4nIOyaCiN?x^irQ<(U!tn8jSTUg}0Mfnx*h|IOR*c zmvC&q;>`kF0=9O+1uWK5czY=Vg)A=*fLP&e4eJ_{<)<)P8wziSqIP8@%OA{Y#R_kw z|Hle%k78`=3UB?(c+{c;{)~~gkmctx{S=SyB;5I_{uE<{w|6jgtnhX-W6$vTDEBJ* zr=B6kWcmMQY^?B>4^1ytczZq1Z@TD|^(yA4@ZxtsmcL()+nD+dF;;kc9gCXbl}OXB za#J_4EKA|-iHx_%@?T<2EQPl}XK|Lo+v^!`DZIUb@s`5dJ1DoI@Yeq}66CA!mSp)~ zvBI&!TRzLZb`;+763A`a$LV<^PJAc6>o}12D#-FL;+{P4TRiFWyu%UaB<{q2J}o3i zPp8+tklpUQz0nUD*)`xj((6eH?~7oB{8+*t#{a~2{HsE0eNgyGBz6lA8iKKon?_>y z-7fNUfma4RP-J@mlf2!9$8b;Uy#r?mi6y&}d;czs6KRUm=TD4$qL70O*X?rxVY5n( z+`9WnzwMHK-#N&w0$;ZJ7ZZk7`=wQ&)}CAUrIWk_T9h{rzxQ_?_u{hBF7iD;g2Zuj zI;ITvSg-IdhdtJCjZ0!U`~5vGd${-4xV#N(t7eGHeC<*|Wmiy&gTmICOXrbP1yz!T`8d06UWa;vBw!Yd{37eo_uUJUVV18y()jVn=s8);W7q3_}s{#Ag(e8LQt1>i) zxi`(Fwn2$(-6-mZ!x$=5-!8(}KB^ISk1e0puUNcd85r+;a)3CH2LTaW#d-B}S1h*G zBjKPn% z&58z?mX33EywIHX(q)jwFCbJ&o1wE zlvj>0mWP$Js5~x0+2!p-d3_OPoQYe40K2?*t@?2+V(ND#{3Z{}*sqJ@r0G#-;#Nbp z%i|!$)UOP2rabPaF%PdF-C&eQjT6Q(j=wt;P56P`TIT%Sg9PX+jV9ehqyyb3ax9CE zTr$(AC@(>}hv-pf(h<=`B6kbcz8ewPo^ISy^q1LDuIx6mg0tGPY(+nhU5j%y8#qd`E+6t`A~2+EN&!(6%NdSg+W?Z~~H0gAkDDQ6T%0(;{Iov(PY zBHNdHDAy3tzxaINJ|*wFR`<5CM|vYhSnS ze7*?rIuG9&1E9;ihtqi6px><;dHaq^*aYQXD)&733!0mttMQT7yKEwEc%6Qt@7#vp zHLur2`>D{`xavmPQ!TB#X-8qYX-jQ+xYx>Z5|Fv4Isjpm=}*JoJAJFjmCs+hweh%6 zf3DO{|M6W?i{HMz)ji%@HMT=r-&v(TGFle3)TB{sTK2wcPb5OehYR;zo5YWub)cI; zjM;bP;v@E5Ps0;a_gz2EkOKctNQr&d!!Q!c-FJNk`E}p*uSne5zH3;tzX(NJ`>s_e zFy40^MycOq91GiJPVjxC)qU54@ScwLUB8KBy6;+wgX_NQBRDwsU0(*avhUiH8Fg

%X&Ot0n_nwUkBzTk&PP)-iEYqb=+`IFL1-I@XBo5@ELSeT=xqp`;W95KlZb6 zw4FQAFrcQNvvEV>e8gGU&sa%|%_j%MRbTsCg_qEkO&FQ$Ikx|n}MWXcEw zHS%`Ak4Z)3UUBM2?8wN=*2Nr)wO$$*Gmi53yG>IQ{$pT7)DtnzlzS%v@ZHgD!iQju zHXq#ZB&5p+H(Z$~ZnzWUr7N9RQ!hhJy~!U6+>m3#I&~K5f+)bP4f`=Ob${T7f(4?y zMfednL^nl_ajsowNvozM*jG29%9~IN7*Wkq2C%~c`?&+yAwJRYyj%^CoU~rk%QU@4 z(=TXxx28YT^jDg`tm$7heY;|Z8G46!wDpJ`$~nST({Rd^Y3Z_=>GagusniVq)BmTW z(=%sIPo-u}pFJ~Q?C?3YVIo7%5SG99)0Qr6=e~nFh15CpsB>7cAMfnr-td09H|~wt zk7wr45$kS1___P>z8~tPZ@`ZK5&OgP`gU*Ku8vXZ8wSC~ElOCqKc3#zfvrr>52l@4 z*t{L%-Ow>%&kY^J+u?t+bT4Jc%GV*^W$kz`KhHdWdh^XEq5X}zeCx$=NK=-+7GrGE-}?fc8qJ7CB6yLphwWe3!O$2lO8` z+Ig(SLUFB=KtfN#-0Jv}{PiE5<3Ky>Kh_XHmn)(4AKUd+S%5&l9y6{iO!H2JcZq2( z#q=s0)8rXu`D2<~Ds?bTZWXbZ=K16-rb#6;jcLw6B_m8zkPNY&+m-&KsGnt8&*dZ< zC~0C2$%zb zj{^7g9@G2-OL6ocN1~id@xO#V|Mf(Co-SKG#_AWH<%`oR2S3y z3)5RnQ?$k{rWsJU#WYuQgyh6D|CO;8)6CL;Ot7GQG0h^1axhI=8E`Sp?^D>}jA@Ea zcRyg7yjP9>;{tZdLB=#oaKSaEc_yYkJ~y@cXGWN2JDwa`|M8s?ziKR0Av5F@BbE9~`mQii3NUT}BYm4;q;Cmuv{$%hy&BE=S*;y_H-V3@Z)mCp z^zG^j_pABZUh7F75EyPA#>#^4fR^RK#7o=FdjS-)AaQ`jGH|kXyo@;e=m{_7apJBs zY_^_du5@W+HhCzA>AJ!#=Vhh)6edZ06B=h^oPhu**o))EnU9|3>sXxpq7eca#+!|o zj>ZCL9P#WIQuZHdHGU=!yxuKX2cJZbcBY@R@zNCJ5nt*FFMS;H^1(~fPW?E)F!jsE zOFJMhrRzpw>b?VhxBtqV`gMnw4npJ}(?~6kGH-Ka!|Qz)_+~zM>EH6iOOMCuaz1$J zA}m(Zt_#eIN`JGe?02>mUj>o73FCfY#Mypvg2Ze3i_G5(FMT$ulv^9N=iuR`?I<%J zycD=Wm zJz=m@0mkNw!Pcr5P=vuI@q{HF>H#MO{^u+!#o463w|(DV-+Xf@G11Og zrUvQ+ggdEl@yH|nqx^Eb5W>t;FHu_P0Cj_`-B<5CJ}dFVn+@gKeP8c_-^Vl53s%ur zU*GEaDW85i&K7+t=YWip1j}RCr&uK33U83IXX>>eKJZrafB)F;+Ptx~273f|X?c45^Oi!Iq}E#!@3&9R+Iw|OmmMfkCKbX_g3Fo$s$H?`FEaZ^95 zUaD%|dy7NfyXVh8Ej1AtPfm#iu+&8CzO2LHJSV3n&c|seD{wkbOx_z`OEz zhBWh29z;j-`2Aw?!0YV3 zH^!+SKHVaDJK#5YDC1!;DSEUsaUTKi%A4TS53h+x-lySbo-}@RzXo%O^2&e4@%MR6 zB@qA4#Y3pfFizZ8;Kx{LHc>?H#u>&*M@uJWOfi}VTi!>Hb}W;QRGxRjAsq)c0x|XV zhU3S6X1}wo8S-#Z`8{Wxk!N^A-SD2jbKIJ+Jhw#hW^^d&p^Q{h65|VGtl1E^bjG_^ zj6AQrb)m;AXI&^g;PUJV>q4A_V!o6nBADMb`bC0Gp6dC}?(Ffq0WTALCW{UEsif$J znVP;+hu3PJ*CjwRMr&T=;e7P#JN)$gecXc;OYg+_<=4YXE9WFXx!p_eq{Y?;vTe@)0ei2wFY9%^2dndZ z1rv{fy;uFV_lj05^vUI&_g-b&p5K7MMz;h5&R>qpm$mom4up5P_i71l>;v0-^$3f! zw&%}*K)d&f=FFYFS3C+=@4X_F9qqk(7|C+mp8q^!yWV?sI-?GO?Ri?%&$ajJ^Jt0P zd$j{iw6^EV$whmwM29`vd-b1;i|k2=9fZE@y}E~0>)qa~t5CA+y^@}d_FjoG{C?Ye z^;;B@=iV!xZ}05A3c&Vx@6{hEs}Fmx9^}=C_Fjo$>1gj&6=nAG-m6nMYI5wo+Qstn z+IuCtTL-k~d#{Sf+PzoT^YYuhS5p|<$Gum#QdGWsuZo%2?!Drj*_}NJ8ZGOCJ&9K+*4cZ- z>6`4mk_SM}y;n~&TRwZQKEv*^_9VDl#@T!IZ;b85-YYRSZ}(n(iP>}Ry_(M+$+`FH ztBlRL_i895=ezgn2#V@v?-h--9M*fUKE$ii4|}h8ubRDAX?Ds%-g`A2^>*z^aJ!=v-e8dleiJJ^2_-l(7ip03-FcDPit3iKil*2+0s9Is?HE=67k}N8-l@I?w8`X zlySVC#{Rr~TS-JYzO-bo6ssj6U9IMsX)qHEY?T_=DwWfEynK`o*pP2&scYOqBl=Yh zJo2*!i_VQ(aSG_>jHA8#<4|vVp0}J6{+2WZaX{Om^y75E1pUD4>{UQ$x&Z=BWr^`kvCIk;deq49d%AZ`84v>hR5H zoRL9n!3p->Ks)l;8+9I*S-UEi*n%ss5HDA@i~T}s`lT9vQwMmx=dfNnksj?#KNDMU z<&AHDS;4+SW!% z+}co{S@Bm1`ooq4aTdA__z^>3KN`n=BsO3ic|IqMBd-_xBhwNxS;ZMI8yh!nkt4Mc zkg*jCSytl5eNPvV;ySav-1j6n0suhv!GN9mfqhN`cIt5-;Q9NHYHNZA>k?1oJCp|! z=Yu;oH`%>TJ>rhjX5?*aVhzE6yk4dYnTS0%3U>_3)%n!E*UUXnBmGnxUJ5&5VYh9s z!4qTclRHKepKOO63hsl#-l$=S4{1-L{qrvXV|-#VW+FPCM~GikLv{`U+o#KUPr0gwru1V}oLQ;m0Y~H{ePG zNj!@&KVnRQI?Os8U$7E+!qi;E6#NHb!XnQ5y!a0MyAr=4^kBN6a>aNMhyfJC;>0ar z!?8t365udvQN$h3cP0(ghw~UI2I|8GOgF+4IU6mEg+ssuV$31-2dRr3X3dkdM+YY- zUPP|I1r&o*mHb$OYVU@NCq9P^hAT|0aX2~5I+(POLB*qRz-2oMj;{jyBY31m*rL-8 z{t5gh1XB{vAv~B`#Q$i=FIdhr#l;c@NE1v~Ts%SeJ(!`m!o-i*`7;$aB5^UfS&AE# z_!mkkQ{3ppDdc7=ZcJharJSa?lse4%XdiX-gZYURYq5aF$XORKUa*i{SiId&@Tl0p z0*c&gAM|jT_0N&_T0dB&dNu+SiFXF8RTW~18!0F{%$juxdUcrfbV!Q_bqVYN^n&Ox zYi=$AXK5PW4=WwRH2l}fp2^{~noFW9Z~@i6P)4#mV1 zi&?;(JOw)_E=+%`ZB^c!#9vwZqcr^y6qhHql4byk!S|Gu z1&L=^-V-X{vP2Whds1-~iIJ>XbeMG|xd>2VpZJj|ly(CG;t%3q^xO!%_zzqjA3Ri>CSdQyf*&RyV+&>l6oRXs zJO^n5gCNF|^IbNcyv=uF3zM|D5uBm(j!32{9AgffDcU*Ax|-P>V-Axj%{k2aNb(;Eo<*C(;+F^E60o-iaoa=!4SEGcwQ%uW43MpMLxZU1vt!l9}`+Yv5sP_F$W$$666LHV<{%r z2I_+iL6Rs{aBjd?6UV@RQMd&Z&tz(A%;9P#wt(V4vjfi8y}c;;b;epiF=XBgf{$Xv z1;uNUt0*}WPz){%1_C!b{b_Au);6y*SlwD%G?2I`Mwe6^G;hgo0FnmC79KS*)T zVb&Kj-Z{+rBE~z1S??m>&0*HD8<8O2!>oh%2T6G<<@UJ5i_^m-@-iIj z#aqDh5@>!j@1s{RRFCDoipwO2S$~j??i-+31lbs$xev3B{WlW$gF_yTK3E-QU506o z9|!NRkU>fj6Par5nC2}lo_{u+0dh7?ZAm7Z(VRFl50q?cY7W zq;zWajK2ou}P z+qVE8+g#n!P}gX<`o=?j_O=~Dht`^Y-)>mB+e&qvakuu$h{S;Ph=!_GjKTUwd8&Dp zRkgKPKwh=1sc}6FM$~PA@%Oq)ST6P||GuUo%!6#e+CzU@$L|WybYBVPMUjnXF2JH- zK6vI%dCAy+d}q;RdLO{r#SSOHc(d`$4>@=tUdEBU>MVK0AIlMD9LuH4#xr+9UJBu?-%$Ka z{kX1W>Hx1-4tbO5(aywW&h2P*d)m@MB`tY@$Abbot;F| z38#Keka$hK$s7tiQZCWb}iI6jD9GS0-iW0mn{ zo{HBu^r&Dg-26K$)ATV-2kbYf{vEL2p!(tMYQJF#UR1n~R*?ptcb=vn(BYeSKOmg% zE*IyVr1>-{;yEwCcH)TgdBt!(jx6fgvweqIGpEDSK3&h}bFX2g@ao{Rh)+%8p06yN zj1}2)_Iqu>FP}MUpWocJH>haa>v?TI!5*9EV^gfn{h{02_7*`c+HZdMKD^pq@?dkn zz+1VupuDYU=!mxcw5LB9SSa-@y*-q1!G7A=k0Xq6Dddg8HopgZ`(e7aJ+|kn_89E( zdwX_uFi)&`b$e|4^Iy4t5c0mD?d;PQ1-k4w#{DO7R%3_%c^&^8!iIWl z_CgobM_1w;fVTZ!`tgpTEdQBL?t5jSvfuCTxoRKlQCMEzF7?oLaO)~zD%RtT(!yHyMU5#xQO(Y;2SNo_7NR{W%B-#^c|h zpYmo4e}l+aK}-R^0fFDa1;o_CcQay8_$vGs_+^&Cm*QV6Hol-5iDRi1;0j)4;vz

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�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

81Wqb<71CW_;{Ky2MBiI80naO763&>1l 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 E0gsN{S^xk5 diff --git a/ports/cortex_a7/gnu/example_build/libgcc.a b/ports/cortex_a7/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 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 z 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 dhn|!HpOEmEh$!@SiRZoRqCc?z|Ecwv{|7^Je? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/.project b/ports/cortex_a72/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a72/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a72/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..65870ac4 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a72/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a72/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a72/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a72/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a72/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a72/ac6/example_build/tx/.cproject b/ports/cortex_a72/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..10cb82e1 --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a72/ac6/example_build/tx/.project b/ports/cortex_a72/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a72/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a72/ac6/inc/tx_port.h b/ports/cortex_a72/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a72/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a72/ac6/src/tx_initialize_low_level.S b/ports/cortex_a72/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a72/ac6/src/tx_thread_context_restore.S b/ports/cortex_a72/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_context_save.S b/ports/cortex_a72/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a72/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a72/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a72/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a72/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a72/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a72/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a72/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_schedule.S b/ports/cortex_a72/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_stack_build.S b/ports/cortex_a72/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a72/ac6/src/tx_thread_system_return.S b/ports/cortex_a72/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a72/ac6/src/tx_timer_interrupt.S b/ports/cortex_a72/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a72/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a72/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/.project b/ports/cortex_a72/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a72/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a72/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..2b75e0ee --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a72/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a72/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a72/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a72/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a72/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a72/gnu/example_build/tx/.cproject b/ports/cortex_a72/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a72/gnu/example_build/tx/.project b/ports/cortex_a72/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a72/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a72/gnu/inc/tx_port.h b/ports/cortex_a72/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a72/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a72/gnu/src/tx_initialize_low_level.S b/ports/cortex_a72/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_context_restore.S b/ports/cortex_a72/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_context_save.S b/ports/cortex_a72/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a72/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a72/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a72/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a72/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a72/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a72/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a72/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_schedule.S b/ports/cortex_a72/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_stack_build.S b/ports/cortex_a72/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a72/gnu/src/tx_thread_system_return.S b/ports/cortex_a72/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a72/gnu/src/tx_timer_interrupt.S b/ports/cortex_a72/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a72/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a73/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..2d2e59d7 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/.project b/ports/cortex_a73/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a73/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a73/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..98380962 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a73/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a73/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a73/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a73/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a73/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a73/ac6/example_build/tx/.cproject b/ports/cortex_a73/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..1b43e008 --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a73/ac6/example_build/tx/.project b/ports/cortex_a73/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a73/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a73/ac6/inc/tx_port.h b/ports/cortex_a73/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a73/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a73/ac6/src/tx_initialize_low_level.S b/ports/cortex_a73/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a73/ac6/src/tx_thread_context_restore.S b/ports/cortex_a73/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_context_save.S b/ports/cortex_a73/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a73/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a73/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a73/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a73/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a73/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a73/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a73/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_schedule.S b/ports/cortex_a73/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_stack_build.S b/ports/cortex_a73/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a73/ac6/src/tx_thread_system_return.S b/ports/cortex_a73/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a73/ac6/src/tx_timer_interrupt.S b/ports/cortex_a73/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a73/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a73/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/.project b/ports/cortex_a73/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a73/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a73/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..5dbb6ebb --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a73/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a73/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a73/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a73/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a73/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a73/gnu/example_build/tx/.cproject b/ports/cortex_a73/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a73/gnu/example_build/tx/.project b/ports/cortex_a73/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a73/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a73/gnu/inc/tx_port.h b/ports/cortex_a73/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a73/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a73/gnu/src/tx_initialize_low_level.S b/ports/cortex_a73/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_context_restore.S b/ports/cortex_a73/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_context_save.S b/ports/cortex_a73/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a73/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a73/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a73/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a73/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a73/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a73/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a73/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_schedule.S b/ports/cortex_a73/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_stack_build.S b/ports/cortex_a73/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a73/gnu/src/tx_thread_system_return.S b/ports/cortex_a73/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a73/gnu/src/tx_timer_interrupt.S b/ports/cortex_a73/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a73/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a75/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..b8d51c26 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/.project b/ports/cortex_a75/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a75/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a75/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..7313e0a7 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a75/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a75/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a75/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a75/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a75/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a75/ac6/example_build/tx/.cproject b/ports/cortex_a75/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..e9bbc8ae --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a75/ac6/example_build/tx/.project b/ports/cortex_a75/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a75/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a75/ac6/inc/tx_port.h b/ports/cortex_a75/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a75/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a75/ac6/src/tx_initialize_low_level.S b/ports/cortex_a75/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a75/ac6/src/tx_thread_context_restore.S b/ports/cortex_a75/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_context_save.S b/ports/cortex_a75/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a75/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a75/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a75/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a75/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a75/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a75/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a75/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_schedule.S b/ports/cortex_a75/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_stack_build.S b/ports/cortex_a75/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a75/ac6/src/tx_thread_system_return.S b/ports/cortex_a75/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a75/ac6/src/tx_timer_interrupt.S b/ports/cortex_a75/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a75/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a75/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/.project b/ports/cortex_a75/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a75/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a75/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..845e250d --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a75/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a75/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a75/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a75/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a75/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a75/gnu/example_build/tx/.cproject b/ports/cortex_a75/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a75/gnu/example_build/tx/.project b/ports/cortex_a75/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a75/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a75/gnu/inc/tx_port.h b/ports/cortex_a75/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a75/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a75/gnu/src/tx_initialize_low_level.S b/ports/cortex_a75/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_context_restore.S b/ports/cortex_a75/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_context_save.S b/ports/cortex_a75/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a75/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a75/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a75/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a75/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a75/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a75/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a75/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_schedule.S b/ports/cortex_a75/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_stack_build.S b/ports/cortex_a75/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a75/gnu/src/tx_thread_system_return.S b/ports/cortex_a75/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a75/gnu/src/tx_timer_interrupt.S b/ports/cortex_a75/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a75/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a76/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..710476e9 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/.project b/ports/cortex_a76/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a76/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a76/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..edb564a7 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a76/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a76/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a76/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a76/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a76/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a76/ac6/example_build/tx/.cproject b/ports/cortex_a76/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..8f5835ec --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76/ac6/example_build/tx/.project b/ports/cortex_a76/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a76/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a76/ac6/inc/tx_port.h b/ports/cortex_a76/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a76/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a76/ac6/src/tx_initialize_low_level.S b/ports/cortex_a76/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a76/ac6/src/tx_thread_context_restore.S b/ports/cortex_a76/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_context_save.S b/ports/cortex_a76/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a76/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a76/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a76/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a76/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a76/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a76/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a76/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_schedule.S b/ports/cortex_a76/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_stack_build.S b/ports/cortex_a76/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a76/ac6/src/tx_thread_system_return.S b/ports/cortex_a76/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a76/ac6/src/tx_timer_interrupt.S b/ports/cortex_a76/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a76/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a76/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/.project b/ports/cortex_a76/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a76/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a76/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..5aeb827f --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a76/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a76/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a76/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a76/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a76/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a76/gnu/example_build/tx/.cproject b/ports/cortex_a76/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76/gnu/example_build/tx/.project b/ports/cortex_a76/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a76/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a76/gnu/inc/tx_port.h b/ports/cortex_a76/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a76/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a76/gnu/src/tx_initialize_low_level.S b/ports/cortex_a76/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_context_restore.S b/ports/cortex_a76/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_context_save.S b/ports/cortex_a76/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a76/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a76/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a76/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a76/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a76/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a76/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a76/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_schedule.S b/ports/cortex_a76/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_stack_build.S b/ports/cortex_a76/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a76/gnu/src/tx_thread_system_return.S b/ports/cortex_a76/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a76/gnu/src/tx_timer_interrupt.S b/ports/cortex_a76/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a76/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a76ae/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..7d177795 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/.project b/ports/cortex_a76ae/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a76ae/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..fff22f43 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a76ae/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a76ae/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a76ae/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a76ae/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a76ae/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a76ae/ac6/example_build/tx/.cproject b/ports/cortex_a76ae/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..e51aafe2 --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76ae/ac6/example_build/tx/.project b/ports/cortex_a76ae/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a76ae/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a76ae/ac6/inc/tx_port.h b/ports/cortex_a76ae/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a76ae/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a76ae/ac6/src/tx_initialize_low_level.S b/ports/cortex_a76ae/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_context_restore.S b/ports/cortex_a76ae/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_context_save.S b/ports/cortex_a76ae/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a76ae/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a76ae/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_schedule.S b/ports/cortex_a76ae/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_stack_build.S b/ports/cortex_a76ae/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_thread_system_return.S b/ports/cortex_a76ae/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a76ae/ac6/src/tx_timer_interrupt.S b/ports/cortex_a76ae/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a76ae/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a76ae/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/.project b/ports/cortex_a76ae/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a76ae/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..8610641d --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a76ae/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a76ae/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a76ae/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a76ae/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a76ae/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a76ae/gnu/example_build/tx/.cproject b/ports/cortex_a76ae/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a76ae/gnu/example_build/tx/.project b/ports/cortex_a76ae/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a76ae/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a76ae/gnu/inc/tx_port.h b/ports/cortex_a76ae/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a76ae/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a76ae/gnu/src/tx_initialize_low_level.S b/ports/cortex_a76ae/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_context_restore.S b/ports/cortex_a76ae/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_context_save.S b/ports/cortex_a76ae/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a76ae/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a76ae/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_schedule.S b/ports/cortex_a76ae/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_stack_build.S b/ports/cortex_a76ae/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_thread_system_return.S b/ports/cortex_a76ae/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a76ae/gnu/src/tx_timer_interrupt.S b/ports/cortex_a76ae/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a76ae/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/.cproject b/ports/cortex_a77/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..c6e9771f --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/.project b/ports/cortex_a77/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3.h b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a77/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a77/ac6/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..4153312a --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.scat b/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..e5783c7c --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,103 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + } + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 8 * 0x4000 {} + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 8 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables do not need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - do not place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/startup.S b/ports/cortex_a77/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..3952a200 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,779 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + //.global MainApp + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + b __main + diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a77/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a77/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/v8_system.h b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/v8_utils.S b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a77/ac6/example_build/sample_threadx/vectors.S b/ports/cortex_a77/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a77/ac6/example_build/tx/.cproject b/ports/cortex_a77/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..137a756a --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/tx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a77/ac6/example_build/tx/.project b/ports/cortex_a77/ac6/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a77/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a77/ac6/inc/tx_port.h b/ports/cortex_a77/ac6/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a77/ac6/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a77/ac6/src/tx_initialize_low_level.S b/ports/cortex_a77/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..d0b541f1 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =zi_limit // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + +zi_limit: + .quad (Image$$TOP_OF_RAM$$Base) + diff --git a/ports/cortex_a77/ac6/src/tx_thread_context_restore.S b/ports/cortex_a77/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_context_save.S b/ports/cortex_a77/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_fp_disable.c b/ports/cortex_a77/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a77/ac6/src/tx_thread_fp_enable.c b/ports/cortex_a77/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a77/ac6/src/tx_thread_interrupt_control.S b/ports/cortex_a77/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_interrupt_disable.S b/ports/cortex_a77/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_interrupt_restore.S b/ports/cortex_a77/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_schedule.S b/ports/cortex_a77/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_stack_build.S b/ports/cortex_a77/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a77/ac6/src/tx_thread_system_return.S b/ports/cortex_a77/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a77/ac6/src/tx_timer_interrupt.S b/ports/cortex_a77/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a77/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/.cproject b/ports/cortex_a77/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..1c32cb32 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/.project b/ports/cortex_a77/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3.h b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/PPM_AEM.h b/ports/cortex_a77/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.c b/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_a77/gnu/example_build/sample_threadx/sample_threadx.launch b/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..394f6715 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.ld b/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..eec8f12b --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __handler_stack = .; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x4000; + __stack = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 8 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.c b/ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.h b/ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/startup.S b/ports/cortex_a77/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..b71b45f8 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,787 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + + + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + bl GetAffinity + bl GetGICR + mov w20, w0 // Keep a copy for later + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w20 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =__handler_stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w20 + mov w1, #15 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w20 + mov w1, #15 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w20 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w20 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/timer_interrupts.c b/ports/cortex_a77/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports/cortex_a77/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.S b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.h b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/v8_mmu.h b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/v8_system.h b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/v8_utils.S b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports/cortex_a77/gnu/example_build/sample_threadx/vectors.S b/ports/cortex_a77/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports/cortex_a77/gnu/example_build/tx/.cproject b/ports/cortex_a77/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..01bcd509 --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/tx/.cproject @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports/cortex_a77/gnu/example_build/tx/.project b/ports/cortex_a77/gnu/example_build/tx/.project new file mode 100644 index 00000000..863ca5cb --- /dev/null +++ b/ports/cortex_a77/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports/cortex_a77/gnu/inc/tx_port.h b/ports/cortex_a77/gnu/inc/tx_port.h new file mode 100644 index 00000000..33bccbf1 --- /dev/null +++ b/ports/cortex_a77/gnu/inc/tx_port.h @@ -0,0 +1,379 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_thread_timeout_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_thread_timeout_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_thread_timeout_ptr; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifndef TX_DISABLE_INLINE + +/* Define macros, with in-line assembly for performance. */ + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned long long daif_value; + + __asm__ volatile (" MRS %0, DAIF ": "=r" (daif_value) ); + __asm__ volatile (" MSR DAIFSet, 0x3" : : : "memory" ); + return((unsigned int) daif_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int daif_value) +{ + +unsigned long long temp; + + temp = (unsigned long long) daif_value; + __asm__ volatile (" MSR DAIF,%0": : "r" (temp): "memory" ); +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + +#else + +unsigned int _tx_thread_interrupt_disable(void); +unsigned int _tx_thread_interrupt_restore(UINT old_posture); + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports/cortex_a77/gnu/src/tx_initialize_low_level.S b/ports/cortex_a77/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..bf04784e --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address + LDR x1, [x1] // + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_context_restore.S b/ports/cortex_a77/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..994c404d --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,287 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, #0] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BNE __tx_thread_no_preempt_restore // Yes, don't preempt this thread + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, #0] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BNE __tx_thread_preempt_restore // No, preemption needs to happen + + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #248] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, #0] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + /* _tx_thread_current_ptr = TX_NULL; */ + + MOV x0, #0 // NULL value + STR x0, [x1, #0] // Clear current thread pointer + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_context_save.S b/ports/cortex_a77/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..859a1e44 --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_context_save.S @@ -0,0 +1,216 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in EL1, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, #0] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, #0] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, #0] // Store it back in the variable + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, #0] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_fp_disable.c b/ports/cortex_a77/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports/cortex_a77/gnu/src/tx_thread_fp_enable.c b/ports/cortex_a77/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports/cortex_a77/gnu/src/tx_thread_interrupt_control.S b/ports/cortex_a77/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_interrupt_disable.S b/ports/cortex_a77/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_interrupt_restore.S b/ports/cortex_a77/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_schedule.S b/ports/cortex_a77/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..9a7a7262 --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_schedule.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else +__tx_thread_schedule_loop: + LDR x0, [x1, #0] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_schedule_loop // If so, keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Yes! We have a thread to execute. Lockout interrupts and + transfer control to it. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x1, #0] // Setup current thread pointer + + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, #0] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #248] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_stack_build.S b/ports/cortex_a77/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..5b7e945a --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + RET // Return to caller + +// } diff --git a/ports/cortex_a77/gnu/src/tx_thread_system_return.S b/ports/cortex_a77/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..7d42b63d --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_thread_system_return.S @@ -0,0 +1,151 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, #0] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #248] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, #0] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, #0] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, #0] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, #0] // Clear current thread pointer + + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports/cortex_a77/gnu/src/tx_timer_interrupt.S b/ports/cortex_a77/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..5810b5c2 --- /dev/null +++ b/ports/cortex_a77/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + /* if (_tx_timer_time_slice) + { */ + + LDR x3, =_tx_timer_time_slice // Pickup address of time-slice + LDR w2, [x3, #0] // Pickup time-slice + CMP w2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + /* _tx_timer_time_slice--; */ + + SUB w2, w2, #1 // Decrement the time-slice + STR w2, [x3, #0] // Store new time-slice value + + /* Check for expiration. */ + /* if (__tx_timer_time_slice == 0) */ + + CMP w2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + /* _tx_timer_expired_time_slice = TX_TRUE; */ + + LDR x3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV w0, #1 // Build expired value + STR w0, [x3, #0] // Set time-slice expiration flag + + /* } */ + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + //{ + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR w2, [x3, #0] // Pickup time-slice expired flag + CMP w2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR x1, =_tx_timer_expired // Pickup addr of other expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR x3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR w2, [x3, #0] // Pickup the actual flag + CMP w2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + + // }/ + +__tx_timer_not_ts_expiration: + + LDP x29, x30, [sp], #16 // Recover x29, x30 + // } + +__tx_timer_nothing_expired: + + RET // Return to caller + +// } diff --git a/ports/cortex_a8/green/example_build/azure_rtos_workspace.gpj b/ports/cortex_a8/ghs/example_build/azure_rtos_workspace.gpj similarity index 100% rename from ports/cortex_a8/green/example_build/azure_rtos_workspace.gpj rename to ports/cortex_a8/ghs/example_build/azure_rtos_workspace.gpj diff --git a/ports/cortex_a8/green/example_build/reset.arm b/ports/cortex_a8/ghs/example_build/reset.arm similarity index 100% rename from ports/cortex_a8/green/example_build/reset.arm rename to ports/cortex_a8/ghs/example_build/reset.arm diff --git a/ports/cortex_a8/ghs/example_build/sample_threadx.c b/ports/cortex_a8/ghs/example_build/sample_threadx.c new file mode 100644 index 00000000..8c61de06 --- /dev/null +++ b/ports/cortex_a8/ghs/example_build/sample_threadx.c @@ -0,0 +1,369 @@ +/* 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; + + +/* 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", first_unused_memory, 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_a8/green/example_build/sample_threadx.con b/ports/cortex_a8/ghs/example_build/sample_threadx.con similarity index 87% rename from ports/cortex_a8/green/example_build/sample_threadx.con rename to ports/cortex_a8/ghs/example_build/sample_threadx.con index 7a34994c..9a82b794 100644 --- a/ports/cortex_a8/green/example_build/sample_threadx.con +++ b/ports/cortex_a8/ghs/example_build/sample_threadx.con @@ -1,4 +1,4 @@ -target_connection.00000000.title="Simulator connection for ThreadX" +target_connection.00000000.title="Simulator" target_connection.00000000.type="Custom" target_connection.00000000.short_type="Custom" target_connection.00000000.args="simarm -cpu=cortexa8 -fpu -rom" diff --git a/ports/cortex_a8/green/example_build/sample_threadx.gpj b/ports/cortex_a8/ghs/example_build/sample_threadx.gpj similarity index 100% rename from ports/cortex_a8/green/example_build/sample_threadx.gpj rename to ports/cortex_a8/ghs/example_build/sample_threadx.gpj diff --git a/ports/cortex_a9/green/example_build/sample_threadx.ld b/ports/cortex_a8/ghs/example_build/sample_threadx.ld similarity index 81% rename from ports/cortex_a9/green/example_build/sample_threadx.ld rename to ports/cortex_a8/ghs/example_build/sample_threadx.ld index 8d1ab4df..a93f11ea 100644 --- a/ports/cortex_a9/green/example_build/sample_threadx.ld +++ b/ports/cortex_a8/ghs/example_build/sample_threadx.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x10000) : .stack align(16) pad(0x1000) : .free_mem align(16) pad(0x10000) : diff --git a/ports/cortex_a8/green/example_build/sample_threadx_el.gpj b/ports/cortex_a8/ghs/example_build/sample_threadx_el.gpj similarity index 100% rename from ports/cortex_a8/green/example_build/sample_threadx_el.gpj rename to ports/cortex_a8/ghs/example_build/sample_threadx_el.gpj diff --git a/ports/cortex_a7/green/example_build/sample_threadx_el.ld b/ports/cortex_a8/ghs/example_build/sample_threadx_el.ld similarity index 82% rename from ports/cortex_a7/green/example_build/sample_threadx_el.ld rename to ports/cortex_a8/ghs/example_build/sample_threadx_el.ld index 33c0f934..65d9de03 100644 --- a/ports/cortex_a7/green/example_build/sample_threadx_el.ld +++ b/ports/cortex_a8/ghs/example_build/sample_threadx_el.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x1000) : .stack align(16) pad(0x1000) : .eventlog align(16) pad(0x10000) : diff --git a/ports/cortex_a7/green/example_build/tx.gpj b/ports/cortex_a8/ghs/example_build/tx.gpj similarity index 72% rename from ports/cortex_a7/green/example_build/tx.gpj rename to ports/cortex_a8/ghs/example_build/tx.gpj index afbd6bef..a8a7bbb8 100644 --- a/ports/cortex_a7/green/example_build/tx.gpj +++ b/ports/cortex_a8/ghs/example_build/tx.gpj @@ -1,7 +1,6 @@ #!gbuild [Library] -I../../../../common/inc - -I../../../../ports_common_green/inc -I../inc ..\..\..\..\common\inc\tx_api.h ..\..\..\..\common\inc\tx_block_pool.h @@ -16,8 +15,11 @@ ..\..\..\..\common\inc\tx_trace.h ..\..\..\..\common\inc\tx_user_sample.h ..\inc\tx_port.h -..\..\..\..\ports_common_green\inc\tx_el.h -..\..\..\..\ports_common_green\inc\tx_ghs.h +..\inc\tx_el.h +..\inc\tx_ghs.h +..\src\tx_el.c +..\src\tx_ghs.c +..\src\tx_ghse.c ..\src\tx_thread_context_restore.arm ..\src\tx_thread_context_save.arm ..\src\tx_thread_fiq_context_restore.arm @@ -218,66 +220,3 @@ ..\..\..\..\common\src\txe_timer_deactivate.c ..\..\..\..\common\src\txe_timer_delete.c ..\..\..\..\common\src\txe_timer_info_get.c -..\..\..\..\ports_common_green\src\tx_el.c -..\..\..\..\ports_common_green\src\tx_ghs.c -..\..\..\..\ports_common_green\src\tx_ghse.c -..\..\..\..\ports_common_green\src\txr_block_allocate.c -..\..\..\..\ports_common_green\src\txr_block_pool_create.c -..\..\..\..\ports_common_green\src\txr_block_pool_delete.c -..\..\..\..\ports_common_green\src\txr_block_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_block_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_block_release.c -..\..\..\..\ports_common_green\src\txr_byte_allocate.c -..\..\..\..\ports_common_green\src\txr_byte_pool_create.c -..\..\..\..\ports_common_green\src\txr_byte_pool_delete.c -..\..\..\..\ports_common_green\src\txr_byte_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_byte_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_byte_release.c -..\..\..\..\ports_common_green\src\txr_event_flags_create.c -..\..\..\..\ports_common_green\src\txr_event_flags_delete.c -..\..\..\..\ports_common_green\src\txr_event_flags_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_info_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_set.c -..\..\..\..\ports_common_green\src\txr_event_flags_set_notify.c -..\..\..\..\ports_common_green\src\txr_ghs.c -..\..\..\..\ports_common_green\src\txr_mutex_create.c -..\..\..\..\ports_common_green\src\txr_mutex_delete.c -..\..\..\..\ports_common_green\src\txr_mutex_get.c -..\..\..\..\ports_common_green\src\txr_mutex_info_get.c -..\..\..\..\ports_common_green\src\txr_mutex_prioritize.c -..\..\..\..\ports_common_green\src\txr_mutex_put.c -..\..\..\..\ports_common_green\src\txr_queue_create.c -..\..\..\..\ports_common_green\src\txr_queue_delete.c -..\..\..\..\ports_common_green\src\txr_queue_flush.c -..\..\..\..\ports_common_green\src\txr_queue_front_send.c -..\..\..\..\ports_common_green\src\txr_queue_info_get.c -..\..\..\..\ports_common_green\src\txr_queue_prioritize.c -..\..\..\..\ports_common_green\src\txr_queue_receive.c -..\..\..\..\ports_common_green\src\txr_queue_send.c -..\..\..\..\ports_common_green\src\txr_queue_send_notify.c -..\..\..\..\ports_common_green\src\txr_semaphore_ceiling_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_create.c -..\..\..\..\ports_common_green\src\txr_semaphore_delete.c -..\..\..\..\ports_common_green\src\txr_semaphore_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_info_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_prioritize.c -..\..\..\..\ports_common_green\src\txr_semaphore_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_put_notify.c -..\..\..\..\ports_common_green\src\txr_thread_create.c -..\..\..\..\ports_common_green\src\txr_thread_delete.c -..\..\..\..\ports_common_green\src\txr_thread_entry_exit_notify.c -..\..\..\..\ports_common_green\src\txr_thread_info_get.c -..\..\..\..\ports_common_green\src\txr_thread_preemption_change.c -..\..\..\..\ports_common_green\src\txr_thread_priority_change.c -..\..\..\..\ports_common_green\src\txr_thread_reset.c -..\..\..\..\ports_common_green\src\txr_thread_resume.c -..\..\..\..\ports_common_green\src\txr_thread_suspend.c -..\..\..\..\ports_common_green\src\txr_thread_terminate.c -..\..\..\..\ports_common_green\src\txr_thread_time_slice_change.c -..\..\..\..\ports_common_green\src\txr_thread_wait_abort.c -..\..\..\..\ports_common_green\src\txr_timer_activate.c -..\..\..\..\ports_common_green\src\txr_timer_change.c -..\..\..\..\ports_common_green\src\txr_timer_create.c -..\..\..\..\ports_common_green\src\txr_timer_deactivate.c -..\..\..\..\ports_common_green\src\txr_timer_delete.c -..\..\..\..\ports_common_green\src\txr_timer_info_get.c diff --git a/ports/cortex_a8/green/example_build/tx_initialize_low_level.arm b/ports/cortex_a8/ghs/example_build/tx_initialize_low_level.arm similarity index 95% rename from ports/cortex_a8/green/example_build/tx_initialize_low_level.arm rename to ports/cortex_a8/ghs/example_build/tx_initialize_low_level.arm index ab71ab46..b44c1cc8 100644 --- a/ports/cortex_a8/green/example_build/tx_initialize_low_level.arm +++ b/ports/cortex_a8/ghs/example_build/tx_initialize_low_level.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Initialize */ /** */ @@ -42,42 +42,42 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, 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 */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -96,7 +96,7 @@ _tx_initialize_low_level: /* _tx_thread_system_stack_ptr = (VOID_PTR) (sp); */ LDR r1,=_tx_thread_system_stack_ptr # Pickup address of system stack ptr - STR sp, [r1] # Save system stack + STR sp, [r1] # Save system stack /* Pickup the first available memory address. */ @@ -146,8 +146,8 @@ _tx_initialize_low_level: STR r0, [r2] # Save first free memory address - /* Setup Timer for periodic interrupts. To generate timer interrupts with - the Green Hills simulator, enter the following command in the target + /* Setup Timer for periodic interrupts. To generate timer interrupts with + the Green Hills simulator, enter the following command in the target window: timer 9999 irq */ /* Done, return to caller. */ @@ -197,7 +197,7 @@ __tx_reserved_handler: .size __tx_reserved_handler,.-__tx_reserved_handler .globl __tx_irq_handler - .globl __tx_irq_processing_return + .globl __tx_irq_processing_return __tx_irq_handler: /* Jump to context save to save system context. */ @@ -209,18 +209,18 @@ __tx_irq_handler: __tx_irq_processing_return: /* At this point execution is still in the IRQ mode. The CPSR, point of - interrupt, and all C scratch registers are available for use. */ + interrupt, and all C scratch registers are available for use. */ #ifdef TX_ENABLE_EVENT_LOGGING MOV r0, 0 # Build interrupt code BL _tx_el_interrupt # Call interrupt event logging #endif - /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start + /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start from IRQ mode with interrupts disabled. This routine switches to the - system mode and returns with IRQ interrupts enabled. - - NOTE: It is very important to ensure all IRQ interrupts are cleared + system mode and returns with IRQ interrupts enabled. + + NOTE: It is very important to ensure all IRQ interrupts are cleared prior to enabling nested IRQ interrupts. */ #ifdef TX_ENABLE_IRQ_NESTING BL _tx_thread_irq_nesting_start @@ -235,7 +235,7 @@ __tx_irq_processing_return: /* Application IRQ handlers can be called here! */ /* If interrupt nesting was started earlier, the end of interrupt nesting - service must be called before returning to _tx_thread_context_restore. + service must be called before returning to _tx_thread_context_restore. This routine returns in processing in IRQ mode with interrupts disabled. */ #ifdef TX_ENABLE_IRQ_NESTING BL _tx_thread_irq_nesting_end @@ -272,12 +272,12 @@ __tx_fiq_processing_return: MOV r0, 1 # Build interrupt code BL _tx_el_interrupt # Call interrupt event logging #endif - - /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start + + /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start from FIQ mode with interrupts disabled. This routine switches to the system mode and returns with FIQ interrupts enabled. - NOTE: It is very important to ensure all FIQ interrupts are cleared + NOTE: It is very important to ensure all FIQ interrupts are cleared prior to enabling nested FIQ interrupts. */ #ifdef TX_ENABLE_FIQ_NESTING BL _tx_thread_fiq_nesting_start diff --git a/ports/cortex_a9/green/example_build/txe.gpj b/ports/cortex_a8/ghs/example_build/txe.gpj similarity index 72% rename from ports/cortex_a9/green/example_build/txe.gpj rename to ports/cortex_a8/ghs/example_build/txe.gpj index 662bcd16..302c0cb2 100644 --- a/ports/cortex_a9/green/example_build/txe.gpj +++ b/ports/cortex_a8/ghs/example_build/txe.gpj @@ -2,7 +2,6 @@ [Library] -DTX_ENABLE_EVENT_LOGGING -I../../../../common/inc - -I../../../../ports_common_green/inc -I../inc ..\..\..\..\common\inc\tx_api.h ..\..\..\..\common\inc\tx_block_pool.h @@ -17,8 +16,11 @@ ..\..\..\..\common\inc\tx_trace.h ..\..\..\..\common\inc\tx_user_sample.h ..\inc\tx_port.h -..\..\..\..\ports_common_green\inc\tx_el.h -..\..\..\..\ports_common_green\inc\tx_ghs.h +..\inc\tx_el.h +..\inc\tx_ghs.h +..\src\tx_el.c +..\src\tx_ghs.c +..\src\tx_ghse.c ..\src\tx_thread_context_restore.arm ..\src\tx_thread_context_save.arm ..\src\tx_thread_fiq_context_restore.arm @@ -219,66 +221,3 @@ ..\..\..\..\common\src\txe_timer_deactivate.c ..\..\..\..\common\src\txe_timer_delete.c ..\..\..\..\common\src\txe_timer_info_get.c -..\..\..\..\ports_common_green\src\tx_el.c -..\..\..\..\ports_common_green\src\tx_ghs.c -..\..\..\..\ports_common_green\src\tx_ghse.c -..\..\..\..\ports_common_green\src\txr_block_allocate.c -..\..\..\..\ports_common_green\src\txr_block_pool_create.c -..\..\..\..\ports_common_green\src\txr_block_pool_delete.c -..\..\..\..\ports_common_green\src\txr_block_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_block_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_block_release.c -..\..\..\..\ports_common_green\src\txr_byte_allocate.c -..\..\..\..\ports_common_green\src\txr_byte_pool_create.c -..\..\..\..\ports_common_green\src\txr_byte_pool_delete.c -..\..\..\..\ports_common_green\src\txr_byte_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_byte_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_byte_release.c -..\..\..\..\ports_common_green\src\txr_event_flags_create.c -..\..\..\..\ports_common_green\src\txr_event_flags_delete.c -..\..\..\..\ports_common_green\src\txr_event_flags_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_info_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_set.c -..\..\..\..\ports_common_green\src\txr_event_flags_set_notify.c -..\..\..\..\ports_common_green\src\txr_ghs.c -..\..\..\..\ports_common_green\src\txr_mutex_create.c -..\..\..\..\ports_common_green\src\txr_mutex_delete.c -..\..\..\..\ports_common_green\src\txr_mutex_get.c -..\..\..\..\ports_common_green\src\txr_mutex_info_get.c -..\..\..\..\ports_common_green\src\txr_mutex_prioritize.c -..\..\..\..\ports_common_green\src\txr_mutex_put.c -..\..\..\..\ports_common_green\src\txr_queue_create.c -..\..\..\..\ports_common_green\src\txr_queue_delete.c -..\..\..\..\ports_common_green\src\txr_queue_flush.c -..\..\..\..\ports_common_green\src\txr_queue_front_send.c -..\..\..\..\ports_common_green\src\txr_queue_info_get.c -..\..\..\..\ports_common_green\src\txr_queue_prioritize.c -..\..\..\..\ports_common_green\src\txr_queue_receive.c -..\..\..\..\ports_common_green\src\txr_queue_send.c -..\..\..\..\ports_common_green\src\txr_queue_send_notify.c -..\..\..\..\ports_common_green\src\txr_semaphore_ceiling_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_create.c -..\..\..\..\ports_common_green\src\txr_semaphore_delete.c -..\..\..\..\ports_common_green\src\txr_semaphore_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_info_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_prioritize.c -..\..\..\..\ports_common_green\src\txr_semaphore_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_put_notify.c -..\..\..\..\ports_common_green\src\txr_thread_create.c -..\..\..\..\ports_common_green\src\txr_thread_delete.c -..\..\..\..\ports_common_green\src\txr_thread_entry_exit_notify.c -..\..\..\..\ports_common_green\src\txr_thread_info_get.c -..\..\..\..\ports_common_green\src\txr_thread_preemption_change.c -..\..\..\..\ports_common_green\src\txr_thread_priority_change.c -..\..\..\..\ports_common_green\src\txr_thread_reset.c -..\..\..\..\ports_common_green\src\txr_thread_resume.c -..\..\..\..\ports_common_green\src\txr_thread_suspend.c -..\..\..\..\ports_common_green\src\txr_thread_terminate.c -..\..\..\..\ports_common_green\src\txr_thread_time_slice_change.c -..\..\..\..\ports_common_green\src\txr_thread_wait_abort.c -..\..\..\..\ports_common_green\src\txr_timer_activate.c -..\..\..\..\ports_common_green\src\txr_timer_change.c -..\..\..\..\ports_common_green\src\txr_timer_create.c -..\..\..\..\ports_common_green\src\txr_timer_deactivate.c -..\..\..\..\ports_common_green\src\txr_timer_delete.c -..\..\..\..\ports_common_green\src\txr_timer_info_get.c diff --git a/ports/cortex_a8/ghs/inc/tx_el.h b/ports/cortex_a8/ghs/inc/tx_el.h new file mode 100644 index 00000000..66cc0d7c --- /dev/null +++ b/ports/cortex_a8/ghs/inc/tx_el.h @@ -0,0 +1,765 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* tx_el.h PORTABLE C/GHS */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the ThreadX event log functions for the GHS MULTI */ +/* EventAnalyzer. It is assumed that tx_api.h and tx_port.h have */ +/* already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef TX_EL_H +#define TX_EL_H + + +/* Define Event Log specific data definitions. */ + +#define TX_EL_VERSION_ID 2 /* Event log version ID */ +#define TX_EL_HEADER_SIZE 24 /* Event log header size */ +#define TX_EL_TNIS 16 /* Number of thread names supported */ + /* If the application needs to */ + /* track more thread names, just */ + /* increase this number and re- */ + /* build the ThreadX library. */ +#define TX_EL_TNI_ENTRY_SIZE 44 /* Thread name entries are 44 bytes */ +#define TX_EL_TNI_NAME_SIZE 34 /* Thread name size in TNI */ +#define TX_EL_NO_MORE_TNI_ROOM 1 /* Error return from thread register*/ +#define TX_EL_NAME_NOT_FOUND 2 /* Error return from un-register */ +#define TX_EL_EVENT_SIZE 32 /* Number of bytes in each event */ +#define TX_EL_VALID_ENTRY 1 /* Valid log entry */ +#define TX_EL_INVALID_ENTRY 0 /* Invalid log entry */ + + +/* Define necessary offsets. */ + +#define TX_EL_TNI_VALID_OFFSET 34 +#define TX_EL_TNI_THREAD_ID_OFFSET 36 +#define TX_EL_TNI_THREAD_PRIORITY_OFF 40 +#define TX_EL_EVENT_TYPE_OFFSET 0 +#define TX_EL_EVENT_SUBTYPE_OFFSET 2 +#define TX_EL_EVENT_TIME_UPPER_OFFSET 4 +#define TX_EL_EVENT_TIME_LOWER_OFFSET 8 +#define TX_EL_EVENT_THREAD_OFFSET 12 +#define TX_EL_EVENT_INFO_1_OFFSET 16 +#define TX_EL_EVENT_INFO_2_OFFSET 20 +#define TX_EL_EVENT_INFO_3_OFFSET 24 +#define TX_EL_EVENT_INFO_4_OFFSET 28 + + +/* Undefine constants that might be been defined previously by tx_api.h. */ + +#undef TX_EL_INITIALIZE +#undef TX_EL_THREAD_REGISTER +#undef TX_EL_THREAD_UNREGISTER +#undef TX_EL_THREAD_STATUS_CHANGE_INSERT +#undef TX_EL_BYTE_ALLOCATE_INSERT +#undef TX_EL_BYTE_POOL_CREATE_INSERT +#undef TX_EL_BYTE_POOL_DELETE_INSERT +#undef TX_EL_BYTE_RELEASE_INSERT +#undef TX_EL_BLOCK_ALLOCATE_INSERT +#undef TX_EL_BLOCK_POOL_CREATE_INSERT +#undef TX_EL_BLOCK_POOL_DELETE_INSERT +#undef TX_EL_BLOCK_RELEASE_INSERT +#undef TX_EL_EVENT_FLAGS_CREATE_INSERT +#undef TX_EL_EVENT_FLAGS_DELETE_INSERT +#undef TX_EL_EVENT_FLAGS_GET_INSERT +#undef TX_EL_EVENT_FLAGS_SET_INSERT +#undef TX_EL_INTERRUPT_CONTROL_INSERT +#undef TX_EL_QUEUE_CREATE_INSERT +#undef TX_EL_QUEUE_DELETE_INSERT +#undef TX_EL_QUEUE_FLUSH_INSERT +#undef TX_EL_QUEUE_RECEIVE_INSERT +#undef TX_EL_QUEUE_SEND_INSERT +#undef TX_EL_SEMAPHORE_CREATE_INSERT +#undef TX_EL_SEMAPHORE_DELETE_INSERT +#undef TX_EL_SEMAPHORE_GET_INSERT +#undef TX_EL_SEMAPHORE_PUT_INSERT +#undef TX_EL_THREAD_CREATE_INSERT +#undef TX_EL_THREAD_DELETE_INSERT +#undef TX_EL_THREAD_IDENTIFY_INSERT +#undef TX_EL_THREAD_PREEMPTION_CHANGE_INSERT +#undef TX_EL_THREAD_PRIORITY_CHANGE_INSERT +#undef TX_EL_THREAD_RELINQUISH_INSERT +#undef TX_EL_THREAD_RESUME_INSERT +#undef TX_EL_THREAD_SLEEP_INSERT +#undef TX_EL_THREAD_SUSPEND_INSERT +#undef TX_EL_THREAD_TERMINATE_INSERT +#undef TX_EL_THREAD_TIME_SLICE_CHANGE_INSERT +#undef TX_EL_TIME_GET_INSERT +#undef TX_EL_TIME_SET_INSERT +#undef TX_EL_TIMER_ACTIVATE_INSERT +#undef TX_EL_TIMER_CHANGE_INSERT +#undef TX_EL_TIMER_CREATE_INSERT +#undef TX_EL_TIMER_DEACTIVATE_INSERT +#undef TX_EL_TIMER_DELETE_INSERT +#undef TX_EL_BLOCK_POOL_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PRIORITIZE_INSERT +#undef TX_EL_BYTE_POOL_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PRIORITIZE_INSERT +#undef TX_EL_EVENT_FLAGS_INFO_GET_INSERT +#undef TX_EL_MUTEX_CREATE_INSERT +#undef TX_EL_MUTEX_DELETE_INSERT +#undef TX_EL_MUTEX_GET_INSERT +#undef TX_EL_MUTEX_INFO_GET_INSERT +#undef TX_EL_MUTEX_PRIORITIZE_INSERT +#undef TX_EL_MUTEX_PUT_INSERT +#undef TX_EL_QUEUE_INFO_GET_INSERT +#undef TX_EL_QUEUE_FRONT_SEND_INSERT +#undef TX_EL_QUEUE_PRIORITIZE_INSERT +#undef TX_EL_SEMAPHORE_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PRIORITIZE_INSERT +#undef TX_EL_THREAD_INFO_GET_INSERT +#undef TX_EL_THREAD_WAIT_ABORT_INSERT +#undef TX_EL_TIMER_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT +#undef TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_QUEUE_SEND_NOTIFY_INSERT +#undef TX_EL_SEMAPHORE_CEILING_PUT_INSERT +#undef TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT +#undef TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT +#undef TX_EL_THREAD_RESET_INSERT +#undef TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT +#undef TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT + + +/* Define Event Types. */ + +#define TX_EL_THREAD_CHANGE 1 +#define TX_EL_INTERRUPT 2 +#define TX_EL_THREADX_CALL 3 +#define TX_EL_USER_EVENT 4 +#define TX_EL_THREAD_STATUS_CHANGE 5 +#define TX_EL_REFRESH 6 /* Not implemented */ +#define TX_EL_TIMER 7 /* Not implemented */ +#define TX_EL_TIMESOURCE_DELTA 8 /* Not implemented */ + + +/* Define TX_EL_THREADX_CALL event sub-types. */ + +#define TX_EL_BYTE_ALLOCATE 0 +#define TX_EL_BYTE_POOL_CREATE 1 +#define TX_EL_BYTE_POOL_DELETE 2 +#define TX_EL_BYTE_RELEASE 3 +#define TX_EL_BLOCK_ALLOCATE 4 +#define TX_EL_BLOCK_POOL_CREATE 5 +#define TX_EL_BLOCK_POOL_DELETE 6 +#define TX_EL_BLOCK_RELEASE 7 +#define TX_EL_EVENT_FLAGS_CREATE 8 +#define TX_EL_EVENT_FLAGS_DELETE 9 +#define TX_EL_EVENT_FLAGS_GET 10 +#define TX_EL_EVENT_FLAGS_SET 11 +#define TX_EL_INTERRUPT_CONTROL 12 +#define TX_EL_QUEUE_CREATE 13 +#define TX_EL_QUEUE_DELETE 14 +#define TX_EL_QUEUE_FLUSH 15 +#define TX_EL_QUEUE_RECEIVE 16 +#define TX_EL_QUEUE_SEND 17 +#define TX_EL_SEMAPHORE_CREATE 18 +#define TX_EL_SEMAPHORE_DELETE 19 +#define TX_EL_SEMAPHORE_GET 20 +#define TX_EL_SEMAPHORE_PUT 21 +#define TX_EL_THREAD_CREATE 22 +#define TX_EL_THREAD_DELETE 23 +#define TX_EL_THREAD_IDENTIFY 24 +#define TX_EL_THREAD_PREEMPTION_CHANGE 25 +#define TX_EL_THREAD_PRIORITY_CHANGE 26 +#define TX_EL_THREAD_RELINQUISH 27 +#define TX_EL_THREAD_RESUME 28 +#define TX_EL_THREAD_SLEEP 29 +#define TX_EL_THREAD_SUSPEND 30 +#define TX_EL_THREAD_TERMINATE 31 +#define TX_EL_THREAD_TIME_SLICE_CHANGE 32 +#define TX_EL_TIME_GET 33 +#define TX_EL_TIME_SET 34 +#define TX_EL_TIMER_ACTIVATE 35 +#define TX_EL_TIMER_CHANGE 36 +#define TX_EL_TIMER_CREATE 37 +#define TX_EL_TIMER_DEACTIVATE 38 +#define TX_EL_TIMER_DELETE 39 +#define TX_EL_BLOCK_POOL_INFO_GET 40 +#define TX_EL_BLOCK_POOL_PRIORITIZE 41 +#define TX_EL_BYTE_POOL_INFO_GET 42 +#define TX_EL_BYTE_POOL_PRIORITIZE 43 +#define TX_EL_EVENT_FLAGS_INFO_GET 44 +#define TX_EL_MUTEX_CREATE 45 +#define TX_EL_MUTEX_DELETE 46 +#define TX_EL_MUTEX_GET 47 +#define TX_EL_MUTEX_INFO_GET 48 +#define TX_EL_MUTEX_PRIORITIZE 49 +#define TX_EL_MUTEX_PUT 50 +#define TX_EL_QUEUE_INFO_GET 51 +#define TX_EL_QUEUE_FRONT_SEND 52 +#define TX_EL_QUEUE_PRIORITIZE 53 +#define TX_EL_SEMAPHORE_INFO_GET 54 +#define TX_EL_SEMAPHORE_PRIORITIZE 55 +#define TX_EL_THREAD_INFO_GET 56 +#define TX_EL_THREAD_WAIT_ABORT 57 +#define TX_EL_TIMER_INFO_GET 58 +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET 59 +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET 60 +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET 61 +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET 62 +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET 63 +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET 64 +#define TX_EL_EVENT_FLAGS_SET_NOTIFY 65 +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET 66 +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET 67 +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET 68 +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET 69 +#define TX_EL_QUEUE_SEND_NOTIFY 70 +#define TX_EL_SEMAPHORE_CEILING_PUT 71 +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET 72 +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET 73 +#define TX_EL_SEMAPHORE_PUT_NOTIFY 74 +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY 75 +#define TX_EL_THREAD_RESET 76 +#define TX_EL_THREAD_PERFORMANCE_INFO_GET 77 +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET 78 +#define TX_EL_THREAD_STACK_ERROR_NOTIFY 79 +#define TX_EL_TIMER_PERFORMANCE_INFO_GET 80 +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET 81 + + +/* Define ThreadX sub-types. */ + +#define TX_EL_INTERRUPT_SUB_TYPE 1 +#define TX_EL_END_OF_INTERRUPT 3 + + +/* Define event logging filters, which may be used by the application program to + dynamically enable/disable events in run-time. */ + +#define TX_EL_FILTER_STATUS_CHANGE 0x0001 +#define TX_EL_FILTER_INTERRUPTS 0x0002 +#define TX_EL_FILTER_THREAD_CALLS 0x0004 +#define TX_EL_FILTER_TIMER_CALLS 0x0008 +#define TX_EL_FILTER_EVENT_FLAG_CALLS 0x0010 +#define TX_EL_FILTER_SEMAPHORE_CALLS 0x0020 +#define TX_EL_FILTER_QUEUE_CALLS 0x0040 +#define TX_EL_FILTER_BLOCK_CALLS 0x0080 +#define TX_EL_FILTER_BYTE_CALLS 0x0100 +#define TX_EL_FILTER_MUTEX_CALLS 0x0200 +#define TX_EL_FILTER_ALL_EVENTS 0xFFFF +#define TX_EL_ENABLE_ALL_EVENTS 0x0000 + + +/* Define filter macros that are inserted in-line with the other macros below. */ + +#ifdef TX_ENABLE_EVENT_FILTERS +#define TX_EL_NO_STATUS_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_STATUS_CHANGE)) { +#define TX_EL_NO_INTERRUPT_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_INTERRUPTS)) { +#define TX_EL_NO_THREAD_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_THREAD_CALLS)) { +#define TX_EL_NO_TIMER_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_TIMER_CALLS)) { +#define TX_EL_NO_EVENT_FLAG_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_EVENT_FLAG_CALLS)) { +#define TX_EL_NO_SEMAPHORE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_SEMAPHORE_CALLS)) { +#define TX_EL_NO_QUEUE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_QUEUE_CALLS)) { +#define TX_EL_NO_BLOCK_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_BLOCK_CALLS)) { +#define TX_EL_NO_BYTE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_BYTE_CALLS)) { +#define TX_EL_NO_MUTEX_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_MUTEX_CALLS)) { +#define TX_EL_END_FILTER } +#else +#define TX_EL_NO_STATUS_EVENTS +#define TX_EL_NO_INTERRUPT_EVENTS +#define TX_EL_NO_THREAD_EVENTS +#define TX_EL_NO_TIMER_EVENTS +#define TX_EL_NO_EVENT_FLAG_EVENTS +#define TX_EL_NO_SEMAPHORE_EVENTS +#define TX_EL_NO_QUEUE_EVENTS +#define TX_EL_NO_BLOCK_EVENTS +#define TX_EL_NO_BYTE_EVENTS +#define TX_EL_NO_MUTEX_EVENTS +#define TX_EL_END_FILTER +#endif + +/* Define externs and constants for non-event log source modules. This is for + the in-line macros below. */ + +#ifndef TX_EL_SOURCE_CODE +extern UCHAR *_tx_el_tni_start; +extern UCHAR **_tx_el_current_event; +extern UCHAR *_tx_el_event_area_start; +extern UCHAR *_tx_el_event_area_end; +extern UINT _tx_el_maximum_events; +extern ULONG _tx_el_total_events; +extern TX_THREAD *_tx_thread_current_ptr; +extern UINT _tx_el_event_filter; +extern ULONG _tx_el_time_base_upper; +extern ULONG _tx_el_time_base_lower; + + +/* Define macros for event logging functions. */ + +#define TX_EL_THREAD_CREATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_THREAD_CREATE, thread_ptr, stack_start, stack_size, priority); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_SET, group_ptr, flags_to_set, set_option); TX_EL_END_FILTER +#define TX_EL_THREAD_DELETE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_DELETE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_TIME_SLICE_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_TIME_SLICE_CHANGE, thread_ptr, thread_ptr -> tx_thread_new_time_slice, new_time_slice); TX_EL_END_FILTER +#define TX_EL_THREAD_TERMINATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_TERMINATE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_SLEEP_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SLEEP, timer_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_SUSPEND_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SUSPEND, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_RELINQUISH_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_RELINQUISH); TX_EL_END_FILTER +#define TX_EL_THREAD_RESUME_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESUME, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PRIORITY_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PRIORITY_CHANGE, thread_ptr, thread_ptr -> tx_thread_priority, new_priority); TX_EL_END_FILTER +#define TX_EL_THREAD_PREEMPTION_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PREEMPTION_CHANGE, thread_ptr, thread_ptr -> tx_thread_preempt_threshold, new_threshold); TX_EL_END_FILTER +#define TX_EL_THREAD_WAIT_ABORT_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_WAIT_ABORT, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_THREAD_ENTRY_EXIT_NOTIFY, thread_ptr, thread_entry_exit_notify); TX_EL_END_FILTER +#define TX_EL_THREAD_RESET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_PERFORMANCE_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_STACK_ERROR_NOTIFY, stack_error_handler); TX_EL_END_FILTER +#define TX_EL_TIME_SET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_SET, new_time); TX_EL_END_FILTER +#define TX_EL_TIME_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_GET, _tx_timer_system_clock); TX_EL_END_FILTER +#define TX_EL_TIMER_DELETE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DELETE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_CREATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate); TX_EL_END_FILTER +#define TX_EL_TIMER_CHANGE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_TIMER_CHANGE, timer_ptr, initial_ticks, reschedule_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_IDENTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_IDENTIFY); TX_EL_END_FILTER +#define TX_EL_TIMER_DEACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DEACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_ACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_ACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_PERFORMANCE_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_GET, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_DELETE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_DELETE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CREATE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_CREATE, semaphore_ptr, initial_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PRIORITIZE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PRIORITIZE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CEILING_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_SEMAPHORE_CEILING_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, ceiling); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT_NOTIFY, semaphore_ptr, semaphore_put_notify); TX_EL_END_FILTER +#define TX_EL_QUEUE_FRONT_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_FRONT_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_RECEIVE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_RECEIVE, queue_ptr, destination_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_FLUSH_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_FLUSH, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_DELETE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_DELETE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_CREATE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_QUEUE_CREATE, queue_ptr, queue_start, queue_size, message_size); TX_EL_END_FILTER +#define TX_EL_QUEUE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PRIORITIZE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PRIORITIZE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PERFORMANCE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_NOTIFY_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND_NOTIFY, queue_ptr, queue_send_notify); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_GET, group_ptr, requested_flags, get_option); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_DELETE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_DELETE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_CREATE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_CREATE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_EVENT_FLAGS_SET_NOTIFY, group_ptr, events_set_notify); TX_EL_END_FILTER +#define TX_EL_BYTE_RELEASE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BYTE_RELEASE, pool_ptr, memory_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_DELETE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_CREATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_POOL_CREATE, pool_ptr, pool_start, pool_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PRIORITIZE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_ALLOCATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_ALLOCATE, pool_ptr, memory_ptr, memory_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_BLOCK_RELEASE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_RELEASE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_DELETE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_CREATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_BLOCK_POOL_CREATE, pool_ptr, pool_start, pool_size, block_size); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PRIORITIZE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_ALLOCATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_ALLOCATE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_MUTEX_CREATE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_MUTEX_CREATE, mutex_ptr, inherit); TX_EL_END_FILTER +#define TX_EL_MUTEX_DELETE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_DELETE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_GET, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PRIORITIZE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PRIORITIZE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PUT_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_PUT, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PERFORMANCE_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER + + +#endif + + +/* Define Event Log function prototypes. */ + +VOID _tx_el_initialize(VOID); +UINT _tx_el_thread_register(TX_THREAD *thread_ptr); +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr); +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4); +VOID _tx_el_thread_running(TX_THREAD *thread_ptr); +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr); +VOID _tx_el_interrupt(UINT interrupt_number); +VOID _tx_el_interrupt_end(UINT interrupt_number); +VOID _tx_el_interrupt_control_call(void); +VOID _tx_el_event_log_on(void); +VOID _tx_el_event_log_off(void); +VOID _tx_el_event_filter_set(UINT filter); + + +/* Define macros that are used inside the ThreadX source code. + If event logging is disabled, these macros will be defined + as white space. */ + +#ifdef TX_ENABLE_EVENT_LOGGING +#ifndef TX_NO_EVENT_INFO +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =\ + (ULONG) e;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#endif +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) +#define TX_EL_THREAD_REGISTER(a) +#define TX_EL_THREAD_UNREGISTER(a) +#define TX_EL_INITIALIZE +#endif + +#endif + diff --git a/ports/cortex_a8/ghs/inc/tx_ghs.h b/ports/cortex_a8/ghs/inc/tx_ghs.h new file mode 100644 index 00000000..ca976916 --- /dev/null +++ b/ports/cortex_a8/ghs/inc/tx_ghs.h @@ -0,0 +1,77 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#ifndef _TX_GHS_H_ +#define _TX_GHS_H_ + +#include +#include +#include +#include + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +extern void *__ghs_GetThreadLocalStorageItem(int specifier); + +/* Thread-local storage routines for Green Hills releases 5.x and beyond. + The following specifiers are used when calling + __ghs_GetThreadLocalStorageItem. + + If __ghs_GetThreadLocalStorageItem is customized to + return a per-thread errno value, define the preprocessor symbol + USE_THREAD_LOCAL_ERRNO in ind_errn.c. + */ + +enum __ghs_ThreadLocalStorage_specifier { + __ghs_TLS_asctime_buff, + __ghs_TLS_tmpnam_space, + __ghs_TLS_strtok_saved_pos, + __ghs_TLS_Errno, + __ghs_TLS_gmtime_temp, + __ghs_TLS___eh_globals, + __ghs_TLS_SignalHandlers +}; +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ +typedef void (*SignalHandler)(int); + +typedef struct +{ + int Errno; /* errno. */ + SignalHandler SignalHandlers[_SIGMAX]; /* signal() buffer. */ + char tmpnam_space[L_tmpnam]; /* tmpnam(NULL) buffer. */ + char asctime_buff[30]; /* . */ + char *strtok_saved_pos; /* strtok() position. */ + struct tm gmtime_temp; /* gmtime() and localtime() buffer. */ + void *__eh_globals; /* Pointer for C++ exception handling. */ +} ThreadLocalStorage; + +ThreadLocalStorage *GetThreadLocalStorage(void); +#endif + + +void __ghsLock(void); +void __ghsUnlock(void); + +int __ghs_SaveSignalContext(jmp_buf); +void __ghs_RestoreSignalContext(jmp_buf); + +/* prototypes for FILE lock routines. */ +void __ghs_flock_file(void *); +void __ghs_funlock_file(void *); +int __ghs_ftrylock_file(void *); +void __ghs_flock_create(void **); +void __ghs_flock_destroy(void *); + +/* prototype for GHS/ThreadX error shell checking. */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal); + +#endif /* _TX_GHS_H_ */ diff --git a/ports/cortex_a8/green/inc/tx_port.h b/ports/cortex_a8/ghs/inc/tx_port.h similarity index 91% rename from ports/cortex_a8/green/inc/tx_port.h rename to ports/cortex_a8/ghs/inc/tx_port.h index c741d5c1..133cc208 100644 --- a/ports/cortex_a8/green/inc/tx_port.h +++ b/ports/cortex_a8/ghs/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-A8/Green Hills */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-A8/GHS */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -78,7 +78,7 @@ #include "tx_ghs.h" -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -114,12 +114,12 @@ typedef unsigned short USHORT; #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #ifdef TX_ENABLE_FIQ_SUPPORT #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ @@ -134,13 +134,13 @@ typedef unsigned short USHORT; /* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ -#define TX_EL_TICKS_PER_SECOND 1000000 +#define TX_EL_TICKS_PER_SECOND 1000000 /* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply - simulate the time-stamp source with a counter. */ + simulate the time-stamp source with a counter. */ -#define read_tbu() _tx_el_time_base_upper -#define read_tbl() ++_tx_el_time_base_lower +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower /* Define the port specific options for the _tx_build_options variable. This variable indicates @@ -174,7 +174,7 @@ typedef unsigned short USHORT; #define TX_INLINE_INITIALIZATION -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -186,16 +186,16 @@ typedef unsigned short USHORT; /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 #define TX_THREAD_EXTENSION_2 ULONG tx_thread_vfp_enable; \ VOID * tx_thread_eh_globals; \ int Errno; /* errno. */ \ char * strtok_saved_pos; /* strtok() position. */ -#define TX_THREAD_EXTENSION_3 +#define TX_THREAD_EXTENSION_3 /* Define the port extensions of the remaining ThreadX objects. */ @@ -209,11 +209,11 @@ typedef unsigned short USHORT; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -243,7 +243,7 @@ typedef unsigned short USHORT; extern void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr); \ __tx_cpp_exception_cleanup(thread_ptr); \ } -#else +#else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) \ { \ #pragma weak __cpp_exception_cleanup \ @@ -281,18 +281,18 @@ typedef unsigned short USHORT; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) m = m & ((ULONG) (-((LONG) m))); \ b = __CLZ32(m); \ - b = 31 - b; + b = 31 - b; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -302,7 +302,7 @@ typedef unsigned short USHORT; unsigned int _tx_thread_interrupt_disable(void); void _tx_thread_interrupt_restore(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); @@ -310,7 +310,7 @@ void _tx_thread_interrupt_restore(unsigned int new_po #else -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER >= 350) @@ -349,7 +349,7 @@ asm int disable_ints(void) MSR CPSR_c,r1 #else #ifdef TX_ENABLE_FIQ_SUPPORT - CPSID if + CPSID if #else CPSID i #endif @@ -395,7 +395,7 @@ void tx_thread_vfp_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A8/Green Hills Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A8/Green Hills Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports/cortex_a8/green/readme_threadx.txt b/ports/cortex_a8/ghs/readme_threadx.txt similarity index 100% rename from ports/cortex_a8/green/readme_threadx.txt rename to ports/cortex_a8/ghs/readme_threadx.txt diff --git a/ports/cortex_a8/ghs/src/tx_el.c b/ports/cortex_a8/ghs/src/tx_el.c new file mode 100644 index 00000000..d8f056d7 --- /dev/null +++ b/ports/cortex_a8/ghs/src/tx_el.c @@ -0,0 +1,1165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_EL_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_el.h" +#include "string.h" + + +/* Define global variables used to manage the event pool. */ + +UCHAR *_tx_el_tni_start; +UCHAR **_tx_el_current_event; +UCHAR *_tx_el_event_area_start; +UCHAR *_tx_el_event_area_end; +UINT _tx_el_maximum_events; +ULONG _tx_el_total_events; +UINT _tx_el_event_filter; +ULONG _tx_el_time_base_upper; +ULONG _tx_el_time_base_lower; + +extern char __ghsbegin_eventlog[]; +extern char __ghsend_eventlog[]; + +extern TX_THREAD *_tx_thread_current_ptr; +UINT _tx_thread_interrupt_control(UINT new_posture); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the Event Log (in the format dictated by the */ +/* GHS Event Analyzer) and sets up various information for subsequent */ +/* operation. The start and end of the Event Log is determined by the */ +/* .eventlog section in the linker control file. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_initialize(VOID) +{ + +UCHAR *work_ptr; +UCHAR *read_ptr; +ULONG event_log_size; +UCHAR *end_ptr; +UINT i; + + + /* Clear total event counter. */ + _tx_el_total_events = 0; + + /* Clear event filter. */ + _tx_el_event_filter = 0; + + /* First, pickup the starting and ending address of the Event Log memory. */ + work_ptr = (unsigned char *) __ghsbegin_eventlog; + end_ptr = (unsigned char *) __ghsend_eventlog; + + /* Calculate the event log size. */ + event_log_size = end_ptr - work_ptr; + + /* Subtract off the number of bytes in the header and the TNI area. */ + event_log_size = event_log_size - (TX_EL_HEADER_SIZE + + (TX_EL_TNI_ENTRY_SIZE * TX_EL_TNIS)); + + /* Make sure the event log is evenly divisible by the event size. */ + event_log_size = (event_log_size/TX_EL_EVENT_SIZE) * TX_EL_EVENT_SIZE; + + /* Build the Event Log header. */ + + /* Setup the Event Log Version ID. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_VERSION_ID; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the TNIS (number of thread names) field. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_TNIS; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the EVPS (event pool size) field. */ + *((ULONG *) work_ptr) = event_log_size; + work_ptr = work_ptr + sizeof(ULONG); + + /* Remember the maximum number of events. */ + _tx_el_maximum_events = event_log_size/TX_EL_EVENT_SIZE; + + /* Setup max_events field. */ + *((ULONG *) work_ptr) = _tx_el_maximum_events; + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup the evploc (location of event pool). */ + *((ULONG *) work_ptr) = (ULONG) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Save the current event pointer. */ + _tx_el_current_event = (UCHAR **) work_ptr; + + /* Setup event_ptr (pointer to oldest event) field to the start + of the event pool. */ + *_tx_el_current_event = (UCHAR *) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup tbfreq (the number of ticks in a second) field. */ + *((ULONG *) work_ptr) = TX_EL_TICKS_PER_SECOND; + work_ptr = work_ptr + sizeof(ULONG); + + /* At this point we are pointing at the Thread Name Information (TNI) array. */ + + /* Remember the start of this for future updates. */ + _tx_el_tni_start = work_ptr; + + /* Clear the entire TNI array, this is the initial setting. */ + end_ptr = work_ptr + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE); + memset((void *)work_ptr, 0, (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = end_ptr; + + /* At this point, we are pointing at the actual Event Entry area. */ + + /* Remember the start of the actual event log area. */ + _tx_el_event_area_start = work_ptr; + + /* Clear the entire Event area. */ + end_ptr = work_ptr + event_log_size; + memset((void *)work_ptr, 0, event_log_size); + work_ptr = end_ptr; + + /* Save the end pointer for later use. */ + _tx_el_event_area_end = work_ptr; + + /* Setup an entry to resolve all activities from initialization and from + an idle system. */ + work_ptr = _tx_el_tni_start; + read_ptr = (UCHAR *) "Initialization/System Idle"; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID to NULL. */ + *((ULONG *) (_tx_el_tni_start + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) TX_NULL; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (_tx_el_tni_start + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Clear the time base global variables. */ + _tx_el_time_base_upper = 0; + _tx_el_time_base_lower = 0; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_register(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT i; + + + /* First of all, search for a free slot in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is available. */ + if (*(entry_ptr + TX_EL_TNI_VALID_OFFSET) == TX_EL_INVALID_ENTRY) + break; + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Check to see if there were no more valid entries. */ + if (i >= TX_EL_TNIS) + return(TX_EL_NO_MORE_TNI_ROOM); + + /* Otherwise, we have room in the TNI and a valid record. */ + + /* Setup the thread's name. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) thread_ptr; + + /* Setup the thread priority. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_PRIORITY_OFF)) = (ULONG) thread_ptr -> tx_thread_priority; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (entry_ptr + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Thread name has been registered. */ + return(TX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT found; +UINT i, j; + + + /* First of all, search for a match in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is a match. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + found = TX_TRUE; + j = 0; + do + { + + /* Determine if this character is the same. */ + if (*work_ptr != *read_ptr) + { + + /* Set found to false and fall out of the loop. */ + found = TX_FALSE; + break; + } + else if (*work_ptr == 0) + { + + /* Null terminated, just break the loop. */ + break; + } + else + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + } + + /* Increment the character count. */ + j++; + + } while(j < TX_EL_TNIS); + + + /* Was a match found? */ + if (found) + { + + /* Yes, mark the entry as available now. */ + *(entry_ptr + TX_EL_TNI_VALID_OFFSET) = TX_EL_INVALID_ENTRY; + + /* Get out of the loop! */ + break; + } + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Determine status to return. */ + if (found) + return(TX_SUCCESS); + else + return(TX_EL_NAME_NOT_FOUND); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_user_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user event into the event log. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* sub_type Event subtype for kernel call */ +/* info_1 First information field */ +/* info_2 Second information field */ +/* info_3 Third information field */ +/* info_4 Fourth information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT upper_tb; +UCHAR *entry_ptr; + + /* Disable interrupts. */ + TX_DISABLE + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_USER_EVENT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) sub_type; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) info_1; + + /* Store the second info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) = + (ULONG) info_2; + + /* Store the third info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) = + (ULONG) info_3; + + /* Store the fourth info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) = + (ULONG) info_4; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + /* Restore interrupts. */ + TX_RESTORE +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_running PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread change event into the event */ +/* log, which indicates that a context switch is taking place. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_schedule ThreadX scheduler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_running(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) 0; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) thread_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_preempted PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread preempted event into the event */ +/* log, which indicates that an interrupt occurred that made a higher */ +/* priority thread ready for execution. In this case, the previously */ +/* executing thread has an event entered to indicate it is no longer */ +/* running. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_context_restore ThreadX context restore */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_STATUS_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_READY; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt event into the log, which */ +/* indicates the start of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_INTERRUPT_SUB_TYPE; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt end event into the log, which */ +/* indicates the end of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt_end(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_END_OF_INTERRUPT; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_control PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function remaps the tx_interrupt_control service call so that */ +/* it can be tracked in the event log. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt posture */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_interrupt_control Interrupt control service */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_interrupt_control(UINT new_posture) +{ + +TX_INTERRUPT_SAVE_AREA +UINT old_posture; + + + TX_EL_NO_INTERRUPT_EVENTS + + TX_DISABLE + TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_INTERRUPT_CONTROL, _tx_thread_current_ptr, new_posture) + TX_RESTORE + + TX_EL_END_FILTER + + old_posture = _tx_thread_interrupt_control(new_posture); + return(old_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_on PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables all event filters. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_on(void) +{ + + /* Disable all event filters. */ + _tx_el_event_filter = TX_EL_ENABLE_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_off PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets all event filters, thereby turning event */ +/* logging off. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_off(void) +{ + + /* Set all event filters. */ + _tx_el_event_filter = TX_EL_FILTER_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the events filters specified by the user. */ +/* */ +/* INPUT */ +/* */ +/* filter Events to filter */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_filter_set(UINT filter) +{ + + /* Apply the user event filter. */ + _tx_el_event_filter = filter; +} + diff --git a/ports/cortex_a8/ghs/src/tx_ghs.c b/ports/cortex_a8/ghs/src/tx_ghs.c new file mode 100644 index 00000000..30b8054e --- /dev/null +++ b/ports/cortex_a8/ghs/src/tx_ghs.c @@ -0,0 +1,485 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" +#include +#include + +/* Allow these routines to access the following ThreadX global variables. */ +extern ULONG _tx_thread_created_count; +extern TX_THREAD *_tx_thread_created_ptr; +extern TX_THREAD *_tx_thread_current_ptr; + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +/* Thread-local storage routines for Green Hills releases 5.x and above. */ +/* + Thread-Local (Per-Thread) Library Data Retrieval + ================================================ + + __ghs_ThreadLocalStorage_specifier defines all library data items + that the Green Hills libraries allow to be allocated per-thread. + + An implementation can choose which of these data items to allocate + for each thread. For example, an implementation may choose to + allocate an errno value for each thread, but not the strtok_saved_pos + pointer. The application could then use strtok_r instead of strtok for + correct operation. + + To add per-thread library data, define one of the + TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item + or items in each thread control block TX_THREAD. + + If C++ with exceptions is being used, the __eh_globals entry must be + allocated for each thread. This is typically done by default using + TX_THREAD_EXTENSION_1 in tx_port.h. + + If __ghs_GetThreadLocalStorageItem is customized to return a + per-thread errno value, you should also: + + * Customize the System Library for your project + * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in + src/libsys/ind_errn.c + + If you customize the System Library, you should remove ind_thrd.c + from the libsys.gpj subproject. + + */ + +/* Provide global __eh_globals value to support C++ exception handling + outside a thread context. This name also forces this module to be + included in the linked program instead of the ind_thrd.o module from + the System Library libsys.a. + */ +static void *__eh_globals; + +#pragma ghs startnomisra +void *__ghs_GetThreadLocalStorageItem(int specifier) +{ + void *ptlsitem = (void *)0; + switch (specifier) { + case (int)__ghs_TLS_Errno: + /* Set ptslsitem to the address of the per-thread errno value. + The per-thread errno value should have the type int. + + If returning a per-thread errno value, follow the steps + above. + + This item is used by numerous library functions. + */ + break; + case (int)__ghs_TLS_SignalHandlers: + /* Set ptslsitem to the address of the per-thread SignalHandlers + array. The per-thread SignalHandlers array should have the + array type as in the following declaration: + SignalHandler SignalHandlers[_SIGMAX]; + The SignalHandler type and _SIGMAX constant are defined in + ind_thrd.h. + + This item is used by the library functions signal() and + raise(). + */ + break; + case (int)__ghs_TLS_asctime_buff: + /* Set ptslsitem to the address of the per-thread asctime_buff + array. The per-thread asctime_buff array should have the + array type as in the following declaration: + char asctime_buff[30]; + + This item is used by the library functions asctime() and + ctime(). The library provides asctime_r() and ctime_r(), + inherently thread-safe versions of these functions. + */ + break; + case (int)__ghs_TLS_tmpnam_space: + /* Set ptslsitem to the address of the per-thread tmpnam_space + array. The per-thread tmpnam_space array should have the + array type as in the following declaration: + char tmpnam_space[L_tmpnam]; + The constant is defined in + + This item is used by the library function tmpnam() when + passed NULL. The library provides tmpnam_r(), an + inherently thread-safe version of tmpnam(). + */ + break; + case (int)__ghs_TLS_strtok_saved_pos: + /* Set ptslsitem to the address of the per-thread + strtok_saved_pos pointer. The per-thread strtok_saved_pos + pointer should have the type "char *". + + This item is used by the library function strtok(). + The library provides strtok_r(), an inherently thread-safe + version of strtok(). + */ + break; + case (int)__ghs_TLS_gmtime_temp: + /* Set ptslsitem to the address of the per-thread gmtime_temp + value. The per-thread gmtime_temp value should have the + type "struct tm" defined in time.h, included by indos.h. + + This item is used by the library functions gmtime() and + localtime(). The library provides gmtime_r() and + localtime_r(), inherently thread-safe versions of these + functions. + */ + break; + case (int)__ghs_TLS___eh_globals: + /* Set ptslsitem to the address of the per-thread __eh_globals + value. The per-thread __eh_globals value should have the + type "void *". + + This item is used by C++ exception handling. + */ + if (_tx_thread_current_ptr) + ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + ptlsitem = (void *)&__eh_globals; + break; + } + return ptlsitem; +} +#pragma ghs endnomisra +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ + +/* + * ThreadX C and C++ thread-safe library support routines. + * + * This implementation merely tries to guarantee thread safety within + * individual C library calls such as malloc() and free(), but it does + * not attempt to solve the problems associated with the following + * multithreaded issues: + * + * 1. Use of errno. This can be made thread-safe by adding errno + * to TX_THREAD_PORT_EXTENSION and using that within a modified + * version of libsys/ind_errno.c. + * + * 2. Thread safety ACROSS library calls. Certain C library calls either + * return pointers to statically-allocated data structures or maintain + * state across calls. These include strtok(), asctime(), gmtime(), + * tmpnam(NULL), signal(). To make such C library routines thread-safe + * would require adding a ThreadLocalStorage struct to the thread control + * block TX_THREAD. Since relatively few applications make use of these + * library routines, the implementation provided here uses a single, global + * ThreadLocalStorage data structure rather than greatly increasing the size + * of the thread control block TX_THREAD. + * + * The ThreadX global variable _tx_thread_current_ptr points to the + * current thread's control block TX_THREAD. If a ThreadLocalStorage struct + * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage + * should be modified to return &(_tx_thread_current_ptr->tx_tls). + */ + +static ThreadLocalStorage GlobalTLS; + +ThreadLocalStorage *GetThreadLocalStorage() +{ + return &GlobalTLS; +} +#endif + +/* + * Use a global ThreadX mutex to implement thread safety within C and C++ + * library routines. + * + */ +TX_MUTEX __ghLockMutex; + +/* + * Acquire general lock. Blocks until the lock becomes available. + * Use tx_mutex_get to implement __ghsLock + */ +void __ghsLock(void) +{ + tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER); +} + +/* + * Release general lock + * Use tx_mutex_put to implement __ghsUnlock + */ +void __ghsUnlock(void) +{ + tx_mutex_put(&__ghLockMutex); +} + +/* ThreadX Initialization function prototype. */ +void _tx_initialize_kernel_setup(void); + +void __gh_lock_init(void) +{ + /* Initialize the low-level portions of ThreadX. */ + _tx_initialize_kernel_setup(); + + /* Create the global thread lock mutex. */ + tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT); +} + +/* + Saving State Across setjmp() Calls + ================================== + + These routines can be used to save and restore arbitrary state + across calls to setjmp() and longjmp(). +*/ +int __ghs_SaveSignalContext(jmp_buf jmpbuf) +{ + return 0; +} + +/* Restore arbitrary state across a longjmp() */ +void __ghs_RestoreSignalContext(jmp_buf jmpbuf) +{ +} + +#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560) +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +#else + if (_tx_thread_current_ptr) + + /* Return thread-specific __eh_globals pointer. */ + return _tx_thread_current_ptr->tx_thread_eh_globals; + else + /* Return the global __eh_globals pointer. */ + return GlobalTLS.__eh_globals; +#endif +} +#endif + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +#pragma weak __cpp_exception_init +extern void __cpp_exception_init(void **); +#pragma weak __cpp_exception_cleanup +extern void __cpp_exception_cleanup(void **); + +/* __tx_cpp_exception_init retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_init. + */ +void __tx_cpp_exception_init(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_init) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_init(peh_globals); + } +} + +/* __tx_cpp_exception_cleanup retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_cleanup. + */ +void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_cleanup) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_cleanup(peh_globals); + } +} + +/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize + exceptions for the global context. + */ +void __ghs_cpp_exception_init() { + __tx_cpp_exception_init((void *)0); +} + +/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up + exceptions for the global context. + */ +void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + __tx_cpp_exception_cleanup((void *)0); +} +#endif + + +/* + File Locks + ====================== + + These routines can be customized to implement per-file locks to allow + thread-safe I/O. + +*/ + +/* Acquire lock for FILE *addr */ +void __ghs_flock_file(void *addr) +{ + tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER); +} + +/* Release lock for FILE *addr */ +void __ghs_funlock_file(void *addr) +{ + tx_mutex_put((TX_MUTEX *)addr); +} + +/* Non blocking acquire lock for FILE *addr. May return -1 if */ +/* not implemented. Returns 0 on success and nonzero otherwise. */ +int __ghs_ftrylock_file(void *addr) +{ + return -1; +} + +/* Calls to initialize local lock data structures before they */ +/* are used. */ +void __ghs_flock_create(void **addr) +{ + *addr = (void *)(&__ghLockMutex); +} +void __ghs_flock_destroy(void *addr) {} + + +/* + * ThreadX Peak Stack Checking support routines. + * + * All of these routines are called by MULTI's ThreadX-aware debugging + * package to determine the peak stack use for one thread or for all threads. + * + * These routines are included in this file in order to guarantee that they will + * be available while debugging with MULTI. These routines are not referenced by + * any other part of the ThreadX system. + * + * _txs_thread_stack_check: return the peak stack usage for a thread. + * + * _txs_thread_stack_check_2: store the peak stack usage for all threads + * in the tx_thread_stack_size field of each thread + * control block, TX_THREAD. This routine takes + * advantage of the redundancy within the TX_THREAD + * structure since tx_thread_stack_size can be computed + * from the tx_thread_stack_start and tx_thread_stack_end + * fields of TX_THREAD. + * + * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2 + * call by computing the stack size for each + * thread and storing the result in the + * tx_thread_stack_size field of each thread control + * block TX_THREAD. + * + * These three routines do not support architectures such as i960 or StarCore + * where the stack grows up instead of down. + * + */ +#ifndef TX_DISABLE_STACK_CHECKING + +ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr) +{ + CHAR *cp; /* Pointer inside thread's stack. */ + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start; + cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Return the number of bytes from cp up to and including the + end of the stack. */ + return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1); + } + } + return thread_ptr->tx_thread_stack_size; +} + + +int _txs_thread_stack_check_2(void) { + CHAR * cp; /* Pointer inside thread's stack. */ + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end; + ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Store the number of bytes from cp up to and including the + end of the stack in the tx_thread_stack_size field. */ + tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1; + break; + } + + } + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +int _txs_thread_stack_check_2_fixup(void) { + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and + tx_thread_stack_start fields. */ + tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1; + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +#endif /* TX_DISABLE_STACK_CHECKING */ diff --git a/ports/cortex_a8/ghs/src/tx_ghse.c b/ports/cortex_a8/ghs/src/tx_ghse.c new file mode 100644 index 00000000..6369df77 --- /dev/null +++ b/ports/cortex_a8/ghs/src/tx_ghse.c @@ -0,0 +1,49 @@ +/* + * ThreadX C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 560) +#ifdef _WIN32 +/* Windows uses a different linker, so include a stub routine, never called, + to pull in __cpp_exception_init and __cpp_exception_cleanup */ +extern void __cpp_exception_init(void **); +extern void __cpp_exception_cleanup(void **); +void __tx_win32_pull_in_exceptions(void) { + __cpp_exception_init(0); + __cpp_exception_cleanup(0); +} +#else +#pragma ghs reference __cpp_exception_init +#pragma ghs reference __cpp_exception_cleanup +#endif + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +} +#endif diff --git a/ports/cortex_a8/green/src/tx_thread_context_restore.arm b/ports/cortex_a8/ghs/src/tx_thread_context_restore.arm similarity index 94% rename from ports/cortex_a8/green/src/tx_thread_context_restore.arm rename to ports/cortex_a8/ghs/src/tx_thread_context_restore.arm index 934f431a..08926128 100644 --- a/ports/cortex_a8/green/src/tx_thread_context_restore.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,47 +41,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_context_restore(VOID) @@ -117,13 +114,13 @@ _tx_thread_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -135,7 +132,7 @@ _tx_thread_context_restore: __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -200,7 +197,7 @@ __tx_thread_preempt_restore: VSTMDB sp!, {D0-D15} # Save D0-D15 _tx_skip_irq_vfp_save: #endif - + MOV r3, 1 # Build interrupt stack type STMDB sp!, {r3, r4} # Save interrupt stack type and SPSR STR sp, [r0, 8] # Save stack pointer in thread control @@ -225,7 +222,7 @@ _tx_skip_irq_vfp_save: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - + STR r2, [r0, 24] # Save thread's time-slice MOV r2, 0 # Clear value STR r2, [r3] # Disable global time-slice flag diff --git a/ports/cortex_a8/green/src/tx_thread_context_save.arm b/ports/cortex_a8/ghs/src/tx_thread_context_save.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_context_save.arm rename to ports/cortex_a8/ghs/src/tx_thread_context_save.arm index 5ce48c54..a71b5bf5 100644 --- a/ports/cortex_a8/green/src/tx_thread_context_save.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,46 +39,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_context_save(VOID) @@ -93,7 +90,7 @@ _tx_thread_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers #ifdef TX_ENABLE_FIQ_SUPPORT #ifdef TX_BEFORE_ARMV6 @@ -119,7 +116,7 @@ _tx_thread_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -135,7 +132,7 @@ _tx_thread_context_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing __tx_thread_not_nested_save: /* } */ @@ -149,13 +146,13 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, r10, r12, lr} # Store other registers /* Save the current stack pointer in the thread's control block. */ @@ -175,7 +172,7 @@ __tx_thread_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing /* } else @@ -185,7 +182,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit @@ -200,7 +197,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, 16 # Recover saved registers - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing .type _tx_thread_context_save,$function .size _tx_thread_context_save,.-_tx_thread_context_save diff --git a/ports/cortex_a8/green/src/tx_thread_fiq_context_restore.arm b/ports/cortex_a8/ghs/src/tx_thread_fiq_context_restore.arm similarity index 94% rename from ports/cortex_a8/green/src/tx_thread_fiq_context_restore.arm rename to ports/cortex_a8/ghs/src/tx_thread_fiq_context_restore.arm index 53e09caa..48b01928 100644 --- a/ports/cortex_a8/green/src/tx_thread_fiq_context_restore.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_fiq_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -42,47 +42,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_restore Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_restore Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the fiq interrupt context when processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* FIQ ISR Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the fiq interrupt context when processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* FIQ ISR Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_fiq_context_restore(VOID) @@ -114,13 +111,13 @@ _tx_thread_fiq_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_fiq_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -132,7 +129,7 @@ _tx_thread_fiq_context_restore: __tx_thread_fiq_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -205,7 +202,7 @@ __tx_thread_fiq_preempt_restore: VSTMDB sp!, {D0-D15} # Save D0-D15 _tx_skip_fiq_vfp_save: #endif - + MOV r3, 1 # Build interrupt stack type STMDB sp!, {r3, r4} # Save interrupt stack type and SPSR STR sp, [r0, 8] # Save stack pointer in thread control diff --git a/ports/cortex_a8/green/src/tx_thread_fiq_context_save.arm b/ports/cortex_a8/ghs/src/tx_thread_fiq_context_save.arm similarity index 91% rename from ports/cortex_a8/green/src/tx_thread_fiq_context_save.arm rename to ports/cortex_a8/ghs/src/tx_thread_fiq_context_save.arm index 1a34cdae..e9ebcce6 100644 --- a/ports/cortex_a8/green/src/tx_thread_fiq_context_save.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_fiq_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -33,46 +33,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_save Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_save Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_fiq_context_save(VOID) @@ -87,7 +84,7 @@ _tx_thread_fiq_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state CMP r2, 0 # Is this the first interrupt? @@ -102,7 +99,7 @@ _tx_thread_fiq_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -118,7 +115,7 @@ _tx_thread_fiq_context_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing __tx_thread_fiq_not_nested_save: /* } */ @@ -132,16 +129,16 @@ __tx_thread_fiq_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, lr} # Store other registers, Note that we don't - /* # need to save sl and ip since FIQ has - # copies of these registers. Nested + /* # need to save sl and ip since FIQ has + # copies of these registers. Nested # interrupt processing does need to save # these registers. */ @@ -162,7 +159,7 @@ __tx_thread_fiq_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing /* } else @@ -182,15 +179,15 @@ __tx_thread_fiq_idle_system_save: #endif /* Not much to do here, save the current SPSR and LR for possible - use in IRQ interrupted in idle system conditions, and return to + use in IRQ interrupted in idle system conditions, and return to FIQ interrupt processing. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, lr} # Store other registers that will get used - /* # or stripped off the stack in context + /* # or stripped off the stack in context # restore */ - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing .type _tx_thread_fiq_context_save,$function .size _tx_thread_fiq_context_save,.-_tx_thread_fiq_context_save diff --git a/ports/cortex_a8/green/src/tx_thread_fiq_nesting_end.arm b/ports/cortex_a8/ghs/src/tx_thread_fiq_nesting_end.arm similarity index 91% rename from ports/cortex_a8/green/src/tx_thread_fiq_nesting_end.arm rename to ports/cortex_a8/ghs/src/tx_thread_fiq_nesting_end.arm index 176af18b..9e46cd8c 100644 --- a/ports/cortex_a8/green/src/tx_thread_fiq_nesting_end.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_fiq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,48 +41,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_end Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_end Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ -/* processing from system mode back to FIQ mode prior to the ISR */ -/* calling _tx_thread_fiq_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ +/* processing from system mode back to FIQ mode prior to the ISR */ +/* calling _tx_thread_fiq_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_fiq_nesting_start.arm b/ports/cortex_a8/ghs/src/tx_thread_fiq_nesting_start.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_fiq_nesting_start.arm rename to ports/cortex_a8/ghs/src/tx_thread_fiq_nesting_start.arm index 93d45a8e..7ce7d9a8 100644 --- a/ports/cortex_a8/green/src/tx_thread_fiq_nesting_start.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_fiq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,45 +39,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_start Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_start Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_context_save has been called and switches the FIQ */ -/* processing to the system mode so nested FIQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_context_save has been called and switches the FIQ */ +/* processing to the system mode so nested FIQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_interrupt_control.arm b/ports/cortex_a8/ghs/src/tx_thread_interrupt_control.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_interrupt_control.arm rename to ports/cortex_a8/ghs/src/tx_thread_interrupt_control.arm index 3df58bdd..c725b23f 100644 --- a/ports/cortex_a8/green/src/tx_thread_interrupt_control.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_interrupt_control.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,39 +38,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_interrupt_disable.arm b/ports/cortex_a8/ghs/src/tx_thread_interrupt_disable.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_interrupt_disable.arm rename to ports/cortex_a8/ghs/src/tx_thread_interrupt_disable.arm index 6f82915d..8d0b5a25 100644 --- a/ports/cortex_a8/green/src/tx_thread_interrupt_disable.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_interrupt_disable.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,38 +38,38 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_interrupt_restore.arm b/ports/cortex_a8/ghs/src/tx_thread_interrupt_restore.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_interrupt_restore.arm rename to ports/cortex_a8/ghs/src/tx_thread_interrupt_restore.arm index b15a4575..ea4b408a 100644 --- a/ports/cortex_a8/green/src/tx_thread_interrupt_restore.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_interrupt_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -31,39 +31,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_irq_nesting_end.arm b/ports/cortex_a8/ghs/src/tx_thread_irq_nesting_end.arm similarity index 91% rename from ports/cortex_a8/green/src/tx_thread_irq_nesting_end.arm rename to ports/cortex_a8/ghs/src/tx_thread_irq_nesting_end.arm index 03b9e389..a45048e8 100644 --- a/ports/cortex_a8/green/src/tx_thread_irq_nesting_end.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_irq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -40,48 +40,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_end Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_end Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ -/* processing from system mode back to IRQ mode prior to the ISR */ -/* calling _tx_thread_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ +/* processing from system mode back to IRQ mode prior to the ISR */ +/* calling _tx_thread_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_irq_nesting_start.arm b/ports/cortex_a8/ghs/src/tx_thread_irq_nesting_start.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_irq_nesting_start.arm rename to ports/cortex_a8/ghs/src/tx_thread_irq_nesting_start.arm index 0843df24..ac8a63bc 100644 --- a/ports/cortex_a8/green/src/tx_thread_irq_nesting_start.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_irq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,45 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_start Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_start Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_context_save has been called and switches the IRQ */ -/* processing to the system mode so nested IRQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_context_save has been called and switches the IRQ */ +/* processing to the system mode so nested IRQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a8/green/src/tx_thread_schedule.arm b/ports/cortex_a8/ghs/src/tx_thread_schedule.arm similarity index 94% rename from ports/cortex_a8/green/src/tx_thread_schedule.arm rename to ports/cortex_a8/ghs/src/tx_thread_schedule.arm index a2b6971d..6aad5130 100644 --- a/ports/cortex_a8/green/src/tx_thread_schedule.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_schedule.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,48 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_schedule(VOID) @@ -114,7 +111,7 @@ __tx_thread_schedule_loop: /* } while(_tx_thread_execute_ptr == TX_NULL); */ - + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ @@ -137,7 +134,7 @@ __tx_thread_schedule_loop: MOV r0, v1 # Restore temp register #endif - LDR r1, =_tx_thread_current_ptr # Pickup address of current thread + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread STR r0, [r1] # Setup current thread pointer /* Increment the run count for this thread. */ @@ -151,7 +148,7 @@ __tx_thread_schedule_loop: /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - LDR r2, =_tx_timer_time_slice # Pickup address of time slice + LDR r2, =_tx_timer_time_slice # Pickup address of time slice /* # variable */ LDR sp, [r0, 8] # Switch stack pointers STR r3, [r2] # Setup time-slice @@ -203,7 +200,7 @@ _tx_skip_solicited_vfp_restore: .type _tx_thread_schedule,$function .size _tx_thread_schedule,.-_tx_thread_schedule - + #ifdef __VFP__ .globl tx_thread_vfp_enable tx_thread_vfp_enable: diff --git a/ports/cortex_a8/green/src/tx_thread_stack_build.arm b/ports/cortex_a8/ghs/src/tx_thread_stack_build.arm similarity index 96% rename from ports/cortex_a8/green/src/tx_thread_stack_build.arm rename to ports/cortex_a8/ghs/src/tx_thread_stack_build.arm index 9059e14a..63b2951c 100644 --- a/ports/cortex_a8/green/src/tx_thread_stack_build.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_stack_build.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ /* thread_ptr Pointer to thread control blk */ /* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -86,10 +86,10 @@ .globl _tx_thread_stack_build _tx_thread_stack_build: - + /* Build a fake interrupt frame. The form of the fake interrupt stack on the Cortex-A8 should look like the following after it is built: - + Stack Top: 1 Interrupt stack frame type CPSR Initial value for CPSR r0 (a1) Initial value for r0 diff --git a/ports/cortex_a8/green/src/tx_thread_system_return.arm b/ports/cortex_a8/ghs/src/tx_thread_system_return.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_system_return.arm rename to ports/cortex_a8/ghs/src/tx_thread_system_return.arm index 16637956..7b17a2d7 100644 --- a/ports/cortex_a8/green/src/tx_thread_system_return.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_system_return.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -37,47 +37,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_system_return(VOID) @@ -93,7 +90,7 @@ _tx_thread_system_return: LDR r4, =_tx_thread_current_ptr # Pickup address of current ptr LDR r5, [r4] # Pickup current thread pointer - + #ifdef __VFP__ LDR r1, [r5, 144] # Pickup the VFP enabled flag CMP r1, 0 # Is the VFP enabled? diff --git a/ports/cortex_a8/green/src/tx_thread_vectored_context_save.arm b/ports/cortex_a8/ghs/src/tx_thread_vectored_context_save.arm similarity index 92% rename from ports/cortex_a8/green/src/tx_thread_vectored_context_save.arm rename to ports/cortex_a8/ghs/src/tx_thread_vectored_context_save.arm index f777b217..bb0ccad9 100644 --- a/ports/cortex_a8/green/src/tx_thread_vectored_context_save.arm +++ b/ports/cortex_a8/ghs/src/tx_thread_vectored_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,46 +39,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_vectored_context_save Cortex-A8/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_vectored_context_save Cortex-A8/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_vectored_context_save(VOID) @@ -142,7 +139,7 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Note: Minimal context of interrupted thread is already saved. */ @@ -174,7 +171,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit diff --git a/ports/cortex_a8/green/src/tx_timer_interrupt.arm b/ports/cortex_a8/ghs/src/tx_timer_interrupt.arm similarity index 95% rename from ports/cortex_a8/green/src/tx_timer_interrupt.arm rename to ports/cortex_a8/ghs/src/tx_timer_interrupt.arm index 7021f16b..86ea236e 100644 --- a/ports/cortex_a8/green/src/tx_timer_interrupt.arm +++ b/ports/cortex_a8/ghs/src/tx_timer_interrupt.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ @@ -32,43 +32,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-A8/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-A8/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Process timer expiration */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Process timer expiration */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -95,7 +95,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR r3, =_tx_timer_time_slice # Pickup address of time-slice + LDR r3, =_tx_timer_time_slice # Pickup address of time-slice LDR r2, [r3] # Pickup time-slice CMP r2, 0 # Is it non-active? BEQ __tx_timer_no_time_slice # Yes, skip time-slice processing @@ -212,7 +212,7 @@ __tx_timer_dont_activate: /* if (_tx_timer_expired_time_slice) { */ - LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired + LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired LDR r2, [r3] # Pickup the actual flag CMP r2, 0 # See if the flag is set BEQ __tx_timer_not_ts_expiration # No, skip time-slice processing diff --git a/ports/cortex_a8/ghs/src/txr_ghs.c b/ports/cortex_a8/ghs/src/txr_ghs.c new file mode 100644 index 00000000..19572e2b --- /dev/null +++ b/ports/cortex_a8/ghs/src/txr_ghs.c @@ -0,0 +1,84 @@ +/* + * ThreadX API Runtime Error Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +/* #include "tx_ghs.h" */ +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* Customized ThreadX API runtime error support routine. */ + +void _rnerr(int num, int linenum, const char*str, void*ptr, ...); + +/* __ghs_rnerr() + This is the custom runtime error checking routine. + This implementation uses the existing __rnerr() routine. + Another implementation could use the .syscall mechanism, + provided MULTI was modified to understand that. + */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal) { + TX_INTERRUPT_SAVE_AREA + int num; + /* + Initialize the stack levels value. + + Add 3 to account for the calls to _rnerr, __rnerr, and + __ghs_rnerr. + + If the implementation changes, calls to __ghs_rnerr + will not need to be changed. + + Zero is not permitted, so substitute 3 in that case. + */ + num = (stackLevels+3) & 0xf; + if (!num) { + num = 3; + } + /* + Shift the stack levels value to bits 12..15 and + insert the stack trace display value in bit 11. + Bits 0..10 are unused. + */ + num = (num << 12) | (stackTraceDisplay ? 0x800 : 0); + + /* This will mask all interrupts in the RTEC code, which is probably + unacceptable for many targets. */ + TX_DISABLE + _rnerr(num, -1, (const char *)hexVal, (void *)errMsg); + TX_RESTORE +} + + +/* ThreadX thread stack checking runtime support routine. */ + +extern char __ghsbegin_stack[]; +extern TX_THREAD *_tx_thread_current_ptr; + +void __stkchk(void) { + int i; + if(_tx_thread_current_ptr) + { + if((unsigned)(&i) <= + (unsigned)(_tx_thread_current_ptr -> tx_thread_stack_start)) + { + _rnerr(21, -1, 0, 0); + } + } + else + { + if((unsigned)(&i) <= (unsigned)__ghsbegin_stack) + { + _rnerr(21, -1, 0, 0); + } + } +} diff --git a/ports/cortex_a8/gnu/example_build/libc.a b/ports/cortex_a8/gnu/example_build/libc.a deleted file mode 100644 index 5b04fa4ed9b6479f70979f5577dd0705d1f6d1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2447586 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

Q>dP6leU^u3WJIStz4({jFOv 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 z%0D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܌WM5=^-%ZNR&7Q#ao=B{kEbl$jPaTI#aa6G|%gF@OKm4hz!{Q*WL_v_E z^H6KZ0t2~r18EMViSb^5<@guKMpASFjD4xv^)k~mnb%=?5KHWxT7v(HIFeVuq+VIl zdOyZ=j)|)ZrM`%W4A*fp8kd~}d*lngjfV@a%fFyUK`Q-J?pdz-q$pY2AlHbz=_%3G zvbw3<>AsoC@&4VWmgUZ8+2gcqdG2sjnSO6{CvS!-2Ia=H?D3jfom<8TJZy~gVL$tI+DlB9FGf0&GZ29>d$y;C#rf4?8$-x#zuAPw>4>&#dYGLF?; z#mmeZ$2ye6?Wele3iT}LdH zt7GMhBj(10z`1{C>KPGFf8GV`>%lgY8v9n|73@oBibC+&$9iN=t40uIV2Z&;*#nyIV5*UZUVIj z^Ei4HyXpZvRC9n721u3bSt%pejP;tyS`9DLT$IYg-!I65`g1H?d1t@g_E~ z;dU|bM(T+iD(S=$b|Cp5PYREWFPvS8f0grBRmItxpR0-oRmSbJKRT`|KDcuHoH14L z@w1nYtC|3N`0R7%EI+KeD(+u7ZfsRNWA^g+iJ&r`xjxEZJMzTgYe7e{84cVgXABrQ zcfjZ?!;eKDjW17aD!D9uY3U`Ijs4{PndJL#>>K;zS1a@`pQRo!&FWlS7V}Z!p|cY=oz`it@JVp@=}*~N=rz;0Ms-#3 zQKi5qY~lhl@lML@d#28;O&|#5X5;L&eAJzqoRYMmnG$X6Fty9tn;=`Y&X5yF z)an`7^T%9U=vf<|JczejT!DGrZE9a#zn0QT&wub9eQ87U>eY=H=SsNn-DUbx|1yGW zr3AbqE#MN*Ixx{>@jWmuW5U7V9&Lv?Hu>Jg55_F!l)K7D-pG_;Jv0lRlIwvRFS{Q6WZ#s*V(?d7$iL%7n8jjF`Nw#<%W+_R zM2;V^9zRnCi@{?cU?+1ygBfSOTJ!t&JnADy$NI<&n)=$X?AP}u>Kl$Y6HHn&BK-PZ z@%qPm6I0)1ST|Wo}Cs;KB^XRND!=AX(lCj(J~goaYX5%)Ea!f#b;I-2~e5 z5Tp^b!OPe{d%Crf?Lc1I+?ar+9*K*byXX{h*6^Xf3YQXOFdv|`C6hgP?n30-6zWs& zMmyA}zM{y7uptA%ml3h=a10~t6g)RXB|Y%odnxY?9Krm+af)0&+9xQ^Q(UN6tH|S; z`97?8qv94t_Lu43RD4R2uTmM`q4=sI-()hLZ-a=#6vrwatth-V(s_PmKE7ZlUaR;o zigzo1Q}Ic~=M-O36#4<>Q#i<2&%TPo6{{5&DxR%)p5j_XzAs{V;k|(mtNjDT7Zv|k zaRd%l=C4+qt9Yj33PrvUWIj5hBVMEUgd$%yGX6D1zG)=Q%i64u8mFS8}_^xde;d@jn7NyS?@X(=Q)G|{yepHT*pZA-&^5d zw;DWmo?P*~Z3Dm=$I3bT^L$DLD3fQIhd$X+hP(mtrSc!&R)#XiA!eq@1+med_&w99d_ERfNHF`kHQapG?GT-4IVw4hPa zg0LAzMhnLHb{4koc6m?krUgwsjMkEAK~smZW+S5=$Ww0OZuj%ZqB7bivB+sbX9FW& zMjIU_Yf_QQ{+LN>WwLY?YOFGG{A(;>jHBMT+vQhQoECHn`WIPuyL5RPO)#y_WSVui z%L!H^E2AC5K3Ey;BBogxZ3AnuGTMI3X=SvO*hYmi+RvgHnHKZ|OtUgtzLbxgyWRh0 zqLa~50NB&CpybkJT2L8TE2EvticdEyc@FEfGFtX!=cWa1z^3Q4prm5mw4ndWHi~7m zzh_g<-R@VBr}Jq+wQppMqdO8Kll>P~X=SwhGC3-6w;Ne^yGNr-k#)CQ#*SGTt=!F6 z8SNJ4v@+U**n*YO@)L=uz};?C;BGf6aJL&-ce{767VB>Jf7wETyWPmT+a8im87&`4WLi);0XP{gKP274SzvoJL(}Ets+MJB`85Vak+E1`@=Wcg8Q=N=f=InLucF*OeaWdNPFms`d z)|(dedA94^?apM`LK&?;E$F4Jt4Ky0dG2;U#M(R=ZJz!1-0j}W@}7+LlWfkD(Z0;` z-N|VAy>V}4v=JENC)uEr(avFicGlf4cGUxTteR;-YuU+nNJe|mfw;*D&qS}oIpHKU z%D{UQkivY%4^QItK3p*5VKF2(CA8do4{B%GeVK5&lx4Z4UfD%bwq~f5T`W8N)v00O zFInOYDG?5&AMk<9H;{GXHf#e@-4vro(h73?zsu;}RV(Y?bzXP*F!MGh7=0oLt7a`< z9`|2A{)F)xgkeqI$@m=WAdasFZYpSOuM8Is$e4J>RVVRt2b5nKz6Z>c>-(6Dm5adJ@eQuA%9YWqwQ%dMUhuT6HLD25aC4!E3h7uv65U5+~wHy;QJa=28+R6 z*&ra_V1ijJ=1@1|GK-6_)JNp_5$o|YWw02$gtu8!xuC&}^Et5n{@srH@`z*qy5mqs z<9+X;$YX+Os~Hh~eUE$nLl{#zMTdrzqgyrJ<(Qgh>*M|pCh ztS7=wTBG(7wRz0<$c=)KmTww?BbXm3^#O$&1y0lWS&E_wM*Ir3H!6NiQMgg0->$Z3 zf?+?R_74=FQ|wUutzrg4!hYDv2?mNL7$}-xplE`Dq6r3y zCKxE1VBoV_UNph5-%wjL!LUUW3=~Z;P&C0n(F6lU6ATnhFi1KheJw{AoJx^t~v1 zV35DABVGNYU!l(J>8b0tVl4Wng0D)MKcJqBa;?x)aWlUho6Xj?sM;Y0Bun zh*U7Tp9g@^B^}ufqk9i(>59=!T8KWuCUpW;FuHV%+$E#?6k1b8_dM2Y7~O+;!AKb0 ze?t~!boq%68Qs%>T`;;^nPeH=7FMHHS@tY1t@n)XMJPo^m*PCf=pMo$wT$j1OtXwG zr=xU??h)*RWpwM9W*Objuolbc=9tqmx<6xDA*1^%G$V}eSD0oQT_KPhquY<2af~iS z-90h72cte=bY)~MqdS)!vW)KaY}7Kk?90wEx_lK+MmNt6E&C43Ljx?**k6O8Vc*=@_{{*~SC6{9_dE`>V|34B zRc->+8Qdn0(cQ+(j?pb=&5qF(%GNQuJTiSo_chucqkB0k^BCPa|KKgR+|)E@=7w=qRpaAxXU~cA%TAsXah6uzCHtIl9<~YGtTP7WuM9sL zT^?^rT~=~w`jXO(8F@}+9;V}!EqUb`$wpu*WjbhN2;524&$?f{LqQN9Ip%qLM^%%2y#06m)A8ZUe|Y~j0s!gm9O?*%^-9ULC3lzQ^D10|i_->%^-9X{Hfx>qKh3^Im-whPL8z_7?Q21`3@ZCV+yMe-Y z1BLGf3f~PBz8fffH&FO)pzz&5;k$vtcLRm*1`6K|6uuiMd^b?|ZlLhpK;gTA!gm9O z?*%^-9X{Hfx>qKh3^Im-whPL8z_7?Q21`3@ZG=-xWIFJ3f~R< zoZ7;7!+un4;k#jz2xWfZyMe-Y1BLGf3f~PBz8fffH?UK_8}Y(-1Ni|z>pe?xIT8Et zd5WzX{}IJ26hEVQr{aBzj}oy@%JGPLc&efH7?(7um)BF;Q`E+5tpG1EI^)B6|7z&) zp6A9reBwlr3io1e+*kNP7lv}8K;mngJaJT==!5&iZ#B7MUmLum&UcR=X(#%9n>yb@ zKhnW6?nB8Yl8b*{mT~OE3gv`*9%9rN^n=Lhd_kv?MSVek4U6yveGve@pud*Fy+h&) z`W)<@#lE1qE2uB%TTo$FzMzxFVmKTo5Bwww(HHbrS$OCK{3q?lyKv7J^!;c}eL*i_ z&BhlrmA&c<`X+X$RD3~`jG-@R@&aA>g8m+ptS{&#tVXS}?1^4l?|nfxq7;2W^BU!R zK_82ObH1Q2Vw&{@eJw_=o&$AZm3mUi5L07(@$1tb$1^q$x#rlG#a@qNU<`cK) z3%U+jd*KV34EVdt7c^PffWDw_;AU{Xpx@7taK4~dGS!%w+)>;T#w<&`Jv(2}ygT-M zL9b_K=L`Bm*6e&i<0_0pOnnGX&V8AgoiFI&%r3ny4#(0Q6?57|hl zk_Kx_Dw2Ejq}o)ss+Qtk&#Nl?Co5AceqAqUUAYDd+FJD`yL?qU ztiRhNx&PGD;`Z$zv0j$-2yt$4k<#2w=fd##+tx_;g~r9Tb-|oC{e7Fft92IL+*I3m z;mS3&!d(B~gprjKVjVAV}j%P`ns;Mu6`wE zWWZtxl6RJc75C)-pbRWrbYQJUwGv4Gr?RjiaFS;z$IjmiPt2SXFl4z>!yvH zAqfi=4KR)2F#MRmr+n4OW6EGLV128+2(vh?DBnX!;MLFslkYf0c+tTPSdTn9&-LIt zN3$M$TVu*#F*p?T(nc@BES`)szrOo1P-a|+roMVCn=)7o?#9J^G#4~5AHzBL`Sm@7 z`tTZ7`Ui!Et*;HsetplPzC7YgFlo(*@aucp>mTngO#d#!vMIwl-sqyf4M_9rd)Dh8 z=u=zY^;l+o+W%Bva1dbbZxO({qn<;*blUq2s*-U2mV7|9A z-|je+=RBXsD4w7=Pm$*erq?R+n4$e)#Tym3DBi93O~t1apH&pD5#?W1`!9;(cNp=* zFzl>KeLUQ-mm!?4BgFmM=lf2ND%04RQkf#P=GQ-9n+;fvIWCb8SbEe+K#HZ^vgk2fp?K;>vHPA7W6sr$WDa zMF)1%JHz4E{VK?BMEs9B#-Yx~AHIe0{U1M^bs#?2igqx+?aV~u8X}*xjXYuX`faQy z%(LwL?Pnn_$Y0xmJTG3c<(7`4Q6K#rC-r}{W7WmCbl~o{f76$T4lm^l2F5@>MIwHs0YhA60BU-B$9w&gdX?Nhg-pE2g=m3e*5 zdIws@Tp!&*=c=&bq;hOWnM%G!q3GK0+n(cR?e;?F#*oT*3 zx2WBM_Smjyn6=%lX!9gV=Q!NlF)@$&lJ(z~jOmpfpF*A?$P?x--o|Ye|-b ze-U_Z*j3=wKaU@Fd-(zO_>(Tdo2|FwPkPqCec;}3D(-}Eneg?-L!Gi%yh+3K#K}8& zlFqyA?=542;Op3DaW+Coaf=0u6C=17%c8-E4nd3)B2)LGLgdR(07B%600@z9Lqf3- z`3@9S*V6Rm+LaJFX(8GVyN)MBCUxV4$oJA3AvZ-8sUWqGX2sR)Ha%%tAu<^^6(Y}; ze(&{ObdTp-3X$depw3-O-+&5@^f;4!jfJcb`HP;_dm-`&G)f`zVJvN3OaF%LSRwM6 zOtV5{`8>(GmVOrHoe=puw5$;MG1g**$P;K;Au?IlA|W#0g^Lh*2?opwkzoX$YiX(h zJt6Yv*tgl=N1lX$rZoLYSp7EOA0{JS2m1y5M2IXSYlX;b*&!=L{vTFkg~;+={>-h2 z)5(ZgZ-$emk}}lFIT6_TkE`-S2 z*q#+4lZ13aW*we*!tbwcDVOm#wJd1!J%F2j7xo7 zA(Ih*4<$HbW0LgeR|>V(K=G1Uo?mvjBjj_&0k zV=`jCfAfUMLphetwKTt2@LfxDW(8k}d^poR*V6yR;+_z>l(r{CeuB0qME(X__gqVV zk9K!LZ{^?!$i$k8;6P?$c5Q^16GVsyjfMq`y3 zc)J6p`2$!Ck6~nZ`jF=dQzY}EA>8Z3V?`-^+E9L!5Wbh6SCzy#FoI#ttMKl9DkH)g zkkF^1fkCtmzKYxA;8^6wVV>V}hjKd+Cc5GLdKMWg4QkA<*^_a8%&}~((vP{Ad8(wj zq*Snm?ff6`O3g>&x1l>eHXL7HdFeN1RgJ6q>72N#GG4Ikjd<_ORmW?d*XG{)EMS=2q1?G5Uh-3F6zIZ`f#b4WZY#y0LY6 zV^hQ0fa2YTzQ|{-o9G_qM`i=(W2Wh*<@ueQC0U2Nwtz~s9I>l$&t%E?&;g#O>C&N- zA#SCFHkm9rnIpLqz8gc$eYN02ivm4&@?i#E*u(WU=h(d&VS@Q3)er7M>vGZQ)&=W|b)KBsp6%;}5fFPL@O%-V%2vu;^c z*I+!OCta86m|3-WnMQ@lL8tEFopio!4Vv{PE&d0auDIyihj(J~;#EgZtdq}bFo-;# zd0*FbK4-C>4Q0cwY+K6Nl?$gXZiWU;xBJq%waE1F7A|{_pd0p36cOBwt$yC z374W3h~w*OUTT>S7r9`J+PE>2a0RYTyndQs>Kla!T-$888}+d~>tk6{A2pDSH}&1% zUF{HU-(T{5xnA-aEpIA5<3*Uo6N>UpKtsGjn_%)yLxdL{+>ie8set_hEn)k28kS8N zECzo74^1A<1hdHd0l&WL;ck5yZ`QXS%Pcb^Umv9D2 z4D&I(A3wj{$!HhvsAWC4T}-=|Vwv@t@Dl3dx|v|o)+55N?+B}$Tyii5eM2-+8_pJ5V4P*L>v?ZbBWj& zYlz6VP?5(rZE$6H+nAio$qREG`OY3ilXv$-MDl|-;t0iR#bXqwDb7+nTXBWrM#YaQ z$~P3K?{>BCQ+!162a3-rb}0TBVOTUOXDam;W|IoC=~a`L1j^AdWV>-p8hrHB1CV?SJnQsn7_Pj+-WFLNfFJr3V@#=dkrH4-dw5?`Y6 z@RE%cvhx_DMCo3ZO2*|jg!gP_={G!>HLwcF{Vib)BkS_g*LK<7K^C!lc`mnM4nQVl zpP6ZIW@44)nymz$xsh$3TyO}k(fj7m;vr?sFt7w|Vl%u)Ag68ZD!<-=Kf$u)(Ul2z zlPt^5hz*TPGj@w)qZ8>gBczk;h)?toU;hS?snX&6PmVNIl3EM^1AGQ)jK9U{sJ0JnDCgN)WxGl zBfTsYW1y7@Pkj_w)Fn5)bCU_L0v5UCPU=r(zs97)5-XFPh5Hm^m1SkFPEG4Q6TSd7 zk_peDf6;ie9*2amCYV--F>Ru0bus&JL}Ha?2e1!ECRTa&Xr@g{tU+1fLiyZ*u^F8G z9n+3VtRdMOLRd#9Rz4^ngC_U}U6}A?OtUVzC$k0TlKWvMI+xt*S!)kW_#4P0Ot_4! zWx_|X;?t8Zq_VSEuVup7mz{ITy$GvGCj3t~@PeR(3WYO-GsVOb{p3R*vWm^msI z2dT5TpiDRm41ALR$iWPxiSa%b%c(TBe<2g@GLtZCj2fYdrbI7 zR^~C`YgwDegy)&QBNNVxgwKTk3$h6lE++uTg#VSr`^hGZDpI*>7VCxy|0Q}WO!!uI z+cDwSu$*JUuVk%VGvTS9a3wsK+y}C@(;{9wxDWgX(YfTlh?P4g{5YmMCY+8Rz=RK% zvM1%}w!&k=zs6|CgyS;qGvN=jtaHhID9bt~d=xV;iTdMqFs@h^<}Re|x#T{O{i>Ii z*9us~$%X>_?Fzp@_{sWfph6zv6!PU+%;qtM)W5SPQe|liTxe4U3 z9U2V27AyWLcGUyq>y#DIGdOGy5NB9@W5T(KaEkaos@*&F4gAm0*D=53))U1+;gMLw zFcb0w!N5CP@ZU}BxS^|qLun-swYx?@EjP{YDgiZLjwHA8eP@*qoP%%wg$m{yCtvGK z+XRlVytj~u{&@1>HW5upGAxY8`(KPW0}c=kENFsHMVaYJSJ#6|Gp=ZB+rVR4o5baSkX&PupGUfEm^XU|Qm>sEpI!6d*K>QyVr z1NF*DwD&G-=N(4ZTF$xisfk;f+raki4p-3LPPS`(LHCXQm))H+6Pp9C6N;A%u?XlJ znw!sCiCwoIGFb4&tC|6cx_Wut>eY3^0b?nsZEtFCYg}5(Zw5dNu30Mk0f}Aky-U?0 zc*6E3IfD!E7$M^{w9V)X?7Aimx5M0L$8(0$-aaYYixwO0n2ZLRbEXG-#tm1{ZBS( zTaLhR&`E*McXG&hz?c;lo)jaVoSQ(Tx2|bkU2je^%U0FViF&*GIN-UZwoP=?e^1`@ zA6mrRnTFXv^_vSqF-LkA-p}JgBJZwny_O3HZQfJS#;uNo8Hxs&#&8&Z%->VKYUD9x zuo$quv%LtjIISq(79{X$XM)Mcn@cY`xCiUOTXepI<4pJa;^!n#>q~tzXhTD(fSk%Xl zEKOhNB=MKYr&qO>dx3VQ)0d{yiVp4ivY?MEbp9 zecY2=*m=$}d0A{~ActvKOpX~jNZxarz&QVo7gQmLL;v=`yW>Gtu1ka_cs%yV%k8E2 zw?J+e`azwjD7+kOo*Nj?W0iQe;tIu$iXT(FQSo*~;pI^75w(SvgZ-S^^5O^fZ`BrF z4z}=eK;h+p!pi}Lmjenf2NYfoD6f8iH)3|1I3RrQ2ZzZ#g8)ZYg%6XD8m*%%D^%l5Uf|8>VZe9Esu_{ z#g8&@iN-f6iXUaf-=p>;isDBZ@xsdi3_Ji_Hrp3J%0Tg>3=}`gK=Gpt6hF#9@uLjfpykAmGVIT(Eq;_? zKdQF)QHBlit+bQE1&{4bR1`nTu*HuuQ2ZzZf2rxh%V8fg{*>R;_zLdNh!b7? zVr_;K%Omu}i%lATWHs~Ud*RnO!#7H}9Ppfik;Jz}1A~^S<9;1Nn;bx+98QH!8Lpwj zphfhzVer_CC8`DP4}I(#GKcTx?LqZp9n=W7OieV$>0sdtVX!XxBhopqZW+0@hoIwy zwzw=Q|1w+L(MK}4wLP4h&a&GFAIY9RmFiW`G;`@XPEH(lb1Nt(w@Q{1ry1d+JUc2U z$1|pKauc!hf|GjzS;5JD0;f2`$$g(Gj*}B2F5%?<3n^W3a!DzD!QcO0RtxvY*P~v? z$=ytA#6qOu9d8g%p(*Orn2swgCl~YlDAVMoelL9yb?O~hBqz5GrMlqc)-g$Ww@mgk zC}6CzEXhY>^`4XaE=rM;dzO8+oE$x8I!>;XHCs;ZQ|!FuO?*FkG$H~>OZ#{5wm!em~$;rrCPOgqM zot|_dmA#%7Sx%09**Q+G619<&3t7@~a=0nqk&~l_IdYubi7x13xv``HC2CoWYiC)dpOEGH+l zl;z~kU_TuvC-*%)b8`QI^?7%3a+}a2;p9XJ>^QkO93{iLrE=s4t(nPf;mDZOvfNqR zOpcSQ=6E|!ZZOL_PHq`9J5KIO4y5Dc&S$Dqr=Gx6$I0hn0cPcyUQJGn z*sD|6LRd4j4gVx|VQ|nA)|Fvh4N@2D;k^^;lHzreu_hRHEJw!@f0Jni`||&;A^yaf z>bs8kYr}`rSjU8hSi!eYwVo4XPDfQKvFQEM#${N>o`AbZ^4aT!2INuYbAWtiGJ(S{6VYB8T@r6h+4n+>XTA#jr1qd08;*KTI+|<4v{~FK zxQGm$vxMf3nCS&?`wIz&ga_R+;Uth&DkjW`WhaG~0R7@iLI0W5 zltMWsyo=$d3_DcBWm&!wv{X9k+xo6ehScMdm-75Hs;FZoUq{U^rUbkb92JEMQF&o*IKF?yVD-I7(9*( zJatxnPCAN&1y(+RJCUX(1Fe$&_;d?EKW7eeuYyuwEKz2P`= zPjX>b@P*v=3=8oiUx?wff&0gB{Fugb2GidIG=b@ITZ+rAa*5`;4&-$gx2}xAXEUo? z+FKiI`2>R(j`$71{<0J}6qRs&dp%z(_DA`S4mgJ8fD;w>@QlfL?BN-caoEE%Cgbq; z<{3r*hvQUuMxgMEe9*-OiTvWRM8uz@_FTm}jpuY{o$-*>JnnHyl;a=u@IIgmyMW+`(gQ;9dA{KNMEphv7{|P+Jb@L zH1TQGg6*%G#1l{bxULJ(aexS)^U|#qTroV7yT8soS4KpB_i~6nNeasdvHl|3n(%UlxDI=W&B$phV@Cz2jM%ZH60*k56o>dI!uQ-;TF8B7676HZ9USz& zj=c-JI06+XLGWYwpUl@DVnl=JBK*VjYJ3QgcYcT}iSA@dX`Ilb(Pv?$N+XOjr;WXo zP#PV|$P$zGh-68n)&Qdi@h{Aj{Dgol6@-(*V*w0oKRi1<2#etnl3`)`$25+7p8pr6 zC%BaPr45iwhLc$~Tv*x?ghw@T@vI<~;zBBg%^hU8*zdoXaJVF^Z@bSUCQ9Z%*kx}c z;eIT+b4gAM@*dE>+B-5FKX>e*;}4y9X*6Qc#Q37hiJ1#lRn0l+h!vT;t2bPHNmYE- z`gq{_%r)^|%kc6qK6lwToOM9_@xHvPcYho&|J@%)J=var#?lAyTFK00S)*7YtKwem33>@w63ggUjuJXy$zjx zqwMd6X5TD+=WtKfJ!|r`#e9738HFEhT$3b_sqh2S7!Jda`FqO8`vFr1i{RsfU>z4U zn6zoI%`%fVAs>%I6HHq1JBP=x9*-jvPkOk%*!7r?Wm5)=!FqJ`4lZagX(z+REsYIV z;Do{TVtqt2F7;S8Ww02GMZ#DvXkb2u;&%?$qCPxMNqszBoBE31IV?s(B`eTi+A4nM zaI4opEZX`m!?G#EDlSERc`j%$X&Yes>wA~izX3=y{ktB^EW_=`@CEkIR05le{B2PL zNceF{@7_6-VPo~?ox`EX_kgJ!Xu3eeE@Hz%3@!znJ-u_-i2bFrakdrdf@z25?s(^b zTWv3dvd=tiJ7E_j&pYwXVW76ob4O?2D33%s_YMB`3I6bwyENJt4el~WzecNDyvszg zZTNnH>lEQV5|7mg`wI8rF8vB&P=+!cI6`rZBKJ$iPg0zzI9G9j;+cwdiYpY`6)#r2 zf`}dJD#cG}{H==rrudTL|07~ORuNH-=L?sPM%!+;q2Rf z!2Xf3*7LN?CY-E?uJE2`mHG2_)w7DfXY<~Iq4-&43GUf=FV1T$4j4K8(H^C?9Ew<; zsy~1PQ(y73$~sLqz&aUtPhpm6ufvb$I}=RWG(?zXrcsl@quB(rcpM`9d+ili4;JKl z@KVkGaXpHkRj$Jcf>$IH%;L$2D5wvEXU2tS>Z`}HDZ@H$#Q^2Gpuvm_FK~W+EvS$8 zDeNDv#_1d%1^QU79*97_>40FTWcpH24GXA>Zgg$OuC zk>?TG@@xW}srFn&c{V}3JevUPG=7C*v*JaHmnc4=_^jfu6kk{To1)y!qF(XQ13U^t z!2ZlstRdnYvrw^4<5v^0{%^4!q;tD+-p#>k%edg5LGH8qOmo3bY0Rp3;+cjA2zKj< z0?9K?YU{X;vZv=^F3Qa>q%e16D){)eFu!^G-`;xbHzBzl&S4pRwxkU6Sf;jq_C@+= z?9;Q$|9St6UGOEG>Ch90Q7blp4kTv{l!ve?oXD{IaI;~LO1MDc&tZjEAthRke{SMJ zKANOUN8^9=ugHR@tQG(iVzaO_7ED~28c})-vZeAT;eY8VtYtW_PC;e^{(Tg`eq4x6 z3lZHR8+VPVitHDWm>McA!CfOCr&1#p;yXisFG-q+!uz z%u*`SurFhAa6BeEmZ|>4g|SXfn95RAWUNe)9l2Bi`b5xxbR6@M<(lEo}EO? zPFzU0i?N-!kh6WocH+WYnRawiE}uxl)}xtB7*Eeqp&eUk*wI`|=dST7COT=@M_6kQ z(l9=yUlqoSl99F2uo0}-PF%=SR&1qV>`S~;6BpKTgyY8S>nv%dVb`;b;)x4?!lvBB zg`CsxtoRE9YFmQ%HqL>Fhx-BerY62U8J-l~HO6-&MkaeXtF+RvhgnyFyTFVbf??ca66)r;jD4g~8s;*KyGG4?F8a}vyGG6G+%?*&&|0tEHMXH8xQcx$cM=EJ zOcCM@0i(5T=+|_f|G`Ql9`<}j7MhN9<5`h zipQhe{j@!4*mbPTlZIWyjGi=XEYo+ChUHj|Ck^`wvX#Z9VNOo1P8v3V`P{^X{DL&@ zMjG}CMrlAiBFypeB6iZSn_13D!){@xx|W7T&tN5tyT*7$l;a$tv75N?CoJxyVVAOU zCk>m;ROhZS!?JGT!c)0voHUHLcd?U(k=cu#G>o#u*h#|b0x!@kD+o;2(tHs?vh{*&dqk%qycB~GxV8BXEOqgpOvr9tLU z-`+pie?IJ2oAxYE@PJ%BHHy$}0I5prN6vu66aersk%` zagG0Z>QNvS6K*JjmC4_{BydnVw>m%d= zsK^b9)>_fAwXLnTO(|`?iW-$#YmKF?w$y0vOw!}4wzbyV*X^zU?{`0DPJ#%v-nReG zuLmY;ul-ni?Z???pR?9pYi0Gi$oQhtqSA=|q|0}>tBb_9SaGC8evH-U#4yl_b7BZ1 zhddpI!BJ<%5R1JeCMBlC1!lQ(ZzLiVMEH*A{a(v=9({4}rf2$uibyuXqO>s{!~A=U z85t5eKS&XTMfJr4W61*_NzY~4WZeIywMfC_3iY+L$GM{9p%LL1Ogms(wRsyq@ye4X zFq$Whe${IubHWvza?OU1p-=}|v0)``Hzxf7>H|>f2+E0viKGv?q?5HChs(54Jz@Os zL~D^=&6dB_a%2y8bSF5VV};TmSwb7OQ{g*b1P{7~QH3waL& z=cwdBZr-$_cKf=mE1ni9D5gNMX6L$EneO+lWm1#M`iLQxCs+|2#)}AEbZfUHtI`5?61ThJyzR zAN+jY;HjVZmOWbG;(3MeTKv2h^YZE~(>Pf^ev8y)SdRHNcp`LgYOj3M1L2cSUpilJ znZ_ix1C3-m_+60gWxIOIG*a;`dxs{1bRFz1(BwcWE$O|cfW3=qDPwdd1xbO?1oU{bYvP&Am8c8G+sr%(~@ZnL_dQe>R;hgv*;K0 zJKL(~GJ-}9<_oeWF5NGQ2o>A!hl~QZgSblZmof_WmA*t+ zJ!v7N5PLWr)5aD+ev8jV2JsCZ@+Qu~lj=_)aVXOPM=Izjc&>uH@0mVZ!OIj}t6;N& zH!FCTf)6P8Z3UlFkneG%_p*Yng1=HwsEZ(7SaSl*Lsv7uu>S~Ht#Ez;X1uWf2)I|_ z!u})h2NeD-1&=EDBL!Urf32Xf|A=zJ{v)8U_yQ=-WdRENkATAdBj74a)-UWo0{@)C zh5bk1|EX|c{}H&b{|K0Y$xgn79VEbs3ZJf^u>XkoDuoOCkHCffM?hi!5%4LM{&NNS zHG_18{YOAy{}C`7YXaki{YOAy{}E8we*_fv9|8ZM^5Lk1$S>?aB3{^k1Qh480H>;S zVgC_$mBNMnN8rN#BcQPV2q^470t)+&fWrPGps@c4DC|E13j2?M!u}(mu>S}s>^}ku z`;UOa{v)8U{|G4TKLQH-kATAdBcQPV2q^470t)+&fWrPGps@c4DC|E13j2?M!u}(m zu>S}s>^}ku`;UO)To$0P{|G4TKLQH-kAO|7vj2qrN8q1RxUl~S{Aq;?`;WkRpoZfk z>^}ku`;UOru;4IW*nb2R_8$Soxh%l9Rk}Er1w0iCDP?Wx3JxPg{94X$#Pj(^e~qV< z)om22Oq(M@r|>lFL6(-Bwmrx@`-2HIm1`QG&caU`WOX^T-x#{h^+9HLZD?l~<#axe zY>v7AEuX`^OkK%zWxg>d;SUaX;7M#b3f)Q=yp+z{a!+==X=u;mO()uN{0=7*M?nef z@l(p5x}H>HUG%eZ=k4O3hd~N z)PE9zovgt+4-~Y(&Oa)hRuBCNfn6uDkqbdx2<+;KN`c*BEN^}-u$v0RCk1x%1>Zto z$0tCJZ%q0irg{Z-zJ$Q;+aRd1Kzc6As1H`5XVyJ<}8A+YoH5ZL*82<&_bft}6z5(2x& z$Y&3Moi8D}K1^WM50=p-`I8HEj4EP^* zby8sWY4(Jc(D?@3>_r0u-eb?`)QkbQv20RcS3;6Wf!z|8O$zJ|GILU3M=cs(QegKL zqDg_Bu!x%!*nNrhCIxm6v!bNHjusGoNr7EChbSqq`v%J<1$Nw(`jP@W-kCm+z>Y7o zK99ie%WQ;4U{^|N9)aB;;wK91HjpKc!0tAX$?!p7CzjTvz-}|EPYUcRSnNXtc4NU^ zrVj$UXP7Z5uscdJNr4?DF1`;D*a;(p%9f)K0y}yd^Cbm#!-*yZc9d%Sk^;M*Ff}Q# zquWkjQegKRrX~e;HB3zk>~@juq`(fFGsBk@*!_rTQeYP)DUZPJR?_ka?8=$$5!ms2 zjL##mdz$1t0=uV~?h)8+XSzpVx10Ef3GD7fhSL?;`5>@+iwq|PcC_2+`&$Ken5u(# zuVR>l!0u@_`u|E`_Xji(0=qL%pS|%MAVV6k?SVD7JZogS@bWf_`yQxsq<5k*R-VLs z>4OzElDm`00PDA`ia-hj*5{O=2_#*$^uw4GRwtauDc@?C!XDRjSn^<2i{9?ESx2lJD>u*;ThbAo@;axy zxCyCY!tb4uzH2X#Qe?<>UoARfwRBLZ1i_N`gdkCL4@KtE!(I>Z{OV2O*qR{>QL=jl zIW3vQ2j--Hg64b`dRe&F)3pQE*P?y@woh49U0-h$)znsRTeD(ib@i6otBj(gY?3G5 zy!gMitkU~P!G1+tl!M`6l(=f}QIRqUeq-eGjrp+mIN|)&a>RKr=H=B}R(Y~~luhU| zEXRDOW1sS8D8whIzBG;AvdXP!2kk7d9UmsE{49hsO_~Vu>R@kK<@3<|VLy-_q3)Mz zPZBxRNCZ?a0rwqg&SxF%t~6eO@DJx`Pf>UxO$BV?65rIDtA?iThN1v80O zOYHwbcFDft#W_vcCAu?lF)f_HWWTCyE%b&U&BVbg^$?Z-s`Fk;l)RT{Cn68<@39l% zusk|O$TkKq2P@6mm{LA?E}Xa!x=Y=L8gTPCy~&1Qc>kKq2P@6mm{LA?E}X za!x=Y=L8gTPCy~&1pK$3J2^l8?dMMR!#~>3owh$IT^ z6!s{AFIISsf?E{arQm)-Ec&-Ac(;mwRKa>I+Dzwqb6R#=#CzY0E&80}DeYA{6DCR# z>#6Owgt-~PqoY)Sq}-He8YwgFEie5*S*iAWmo$OsJJAkHLRLsSEb7P}4X>o0OmrCN zDLPE+dGIf&&9J0P6N$z}>{Zzq@leEuL0sH*nDg*QJB8zN>hs$N0Rn#eII&a>#-OMf zu(_fl@@$gy+f!uZ9}o#cE$6UYs{J4UKIx4_n!S-vBYzM<`AIKjF$<0w!2&S9(w|tK z#C)Q^t~ z45-XS<%Z!aq@0%Z|?X#f--FaY@p;otUCK0f;?-bt%pxq839w_}Cp z3e0C6ZiK{p>S%@Qcb-b%-ivv8^?rXpSw8N$bs3gpKJKISrPHP&LWi057Jj@-^`(Qo z-`}4_JN}4FY{!Sazf;X5Qxid69qj%7-j4Tt-7kdfKf-EH83c_v=%9QC)TR5m_xt;^ zpcg_O@&{HE^m^+WZv{Q-jq6ME*ZckbxQD+Fdw-{-I;@ESYI;owW4n{MVjliJ?EU>4 zphpLT`eKm3`xMmiA!sH|(ugPR%Ls!+olcsCe5dpNz8d)+(S3*=YuY9{6FITonx82ytT96ICGNM$DwQ7N0H-N!S{81)<|5OuZc&ad_zw> zJ63F5j}?21&@uifI>%!`nmUS(f@dqp`$2 z593*Qcks2|>IT~9bY&Q~xQ20?oAGQkZWxzz2b^2n0*2deg~r(}uCL)X_dG0HX~yz+ zMmYNQv~cu3(mCw>FmB~X<0F?w9vnNI4qt-^_>F~wv55xY7u`9;+leRGG4mWl+ z!X^vh;i{&1Tli?a5q8|$LNCTs!d0=IpqCP!+I1-WWW3FJG2Y~We$~@)6L#zmI}Pzv z(9AFnCT!0eXp6t?=2%*5ZR|Sm#yZG*V+{31_r;4h*bdSfg+8Fo`V-TZcH>#_mZtMTH1IXK zTl5ELOay-c_MH>`dccX^$9UrVjOG~m8ioEl6L}6h$Ixd7U2k9djfR-)ON_&*`V(z$ zYe#=Nn__$0(Vu9`A?JDY^^2nK{0&QEjcBKh{y1Fql<3pL#y*pF^)1oQ`{Eog;R6Bv zdlq;Km9+{R{sHHp>kHq8ew!is$1;}2!MFKCVQ)X$oduoUcP^Gb+5^S{^rLL&VdG`y zLx0xBj6gIV#{AH8<8YO%5BzjvYy_X=kK@2TIOIGP_n}_S5x>(U=18ESHs-_pJY4l; ze6O zqK!M-_r`?m&eV}&hD(xQM48F4Relu6jF?+>X${`Mj7ZKUd0VDDi?NP?Fxos z^pmiJb(`JjPx8PzVwgXuC%)6!7qj5Q;86Pl()=;r-{Ya!oo>L{^Ah$9F9)0h3=1C= zhuU9=A9h}dTeu(E!Y^R#?m!>>Jl^P(i80+7+K0ZueZqDcWnGQoeX+yFQ}Hb4ICxmt z&AFZBJnMqTzOaGR7h^iaXDlM$eHll5l5+>fa$46U__#38|L;tZW2bpN6#g;BY<@TV zH3Wa4`uV@v&ljSf+xNSTG4yRE`n4(=KN7kJ>q4t*g;&PHtp{DPCd`V)(~O02y8(R| zMHpj{fj+?Jndc5UKL+0mx)FxT{X*Oe#^7BJ=oq)f1L)ff%z5^|o{Qkw#FB;=wOIE% z7fL>l827}*7$L7HQ?*a5HAXw<+H&~5kn=2PMC0#Z3=W5WlAwn<61QUTXHi!Z^EK=t zPr*=otDE86LY|%4lzl`M?Lqob%~WXnB$sv=`3~QU77*m3W(h`q4KH$cta!-+*@u zcB?XCjWMig!6VMCXdl-!BY4R9xd^|;``-U{)+WsR zuxx`Kn+uS~KpWB=uJ_S+FuW&*`JY%raBsYvfw`8BKEr-+aE^JvJp`Je&>e0Nv;r7E zJG4`bT`F)3G|0DJOPHVQQLwHthBb0t zh@;Z2P_&Lj zkHrs_y%OhIY&1mK9%u(vc27XMiN$n71s;anC8n`tR&^qu>L)xhDFb^91)scm4?Q z+_weurNzAy>6{mz0%SSwJh>Hd;y$gyeTq53HH-JD6`Im@sQoA6UNv*jkF6VGW?)`7 z?c7;W%$)|_uPmfV(svwtar{~Z4|ul zzULaEc_J@IKtu0CFz23QpZ($k`i#$huD$Gw9JCAbHO{%udx~_v3_2W3)Emz&zI}k< zG~*ZT?=zaQ?%|&5wMOQFP6qZDMtii|j-qc`(f@wC`$1)_Uh?zaqk_ZvRvBJ35P!dikg$oY|~ z`zh3YtJr^W?~>7e@OHct-b6l)_T2+~?YsSk5$(=HAL_C9p>DSGNa&}6mS`vL_WQ_I(*N@dnrZ3h17${Daa7d_~qmiWILrB$a2U^vS6}PiaURGAi?in7vZ8kv;(V z&&rsMBW@XfgWwD-!A2&@*qNG8ABFnmiL8)`ROanF0!1QVre9C{_x(>McgcdF#bZ(t zr!_T46jlxF>7XjRr~Sp8WGYUMd1~;J|Gs8FvkWu_s~Y^$XyXu7gF zbAF8Ia8WJazlJ|%O3FfHvq$rb zLP`m;+UGJc^=bSG_|FS`2T}fWD}X^}6nKu<_agoVS<3I@ z-}LwJ$Gi=HG8oEW86^D~f6wsy{tF<57PqYP*}P2JS*0F8j&*)Od?T46EU%)*?;@u) zRbsxtdgQUDNh}b!k7?6chczlAa39lVTq>|o;0q)^bEUwtP_c>B6xvm@E~a9E-$x>S z4x+zSA>cEu`GN1T^A?N{MU24N#1=}-7x*->MY5_uKpb^mEYmUq_p+)b5(`1$8Q4;Z zWl^ujur85UPT&GkSthaE01bj!mr5)g_<{-SGKu8{W)izxV)=nW=3F7Mg1~u9tCE-# zc$YO)ORO{?4qdO3*rdQid3`e|_W3P>DM)(7v^MrzTeHQmHZeD+%Syt{q+^e2wgO*A zcB@XJd#u2A(y0F*k-FECBeqqhwp#sQAk5k(u>)4%F&4dwgKLkv(+UV3k*g(oHhlyX_%RZvU0M9y_1@DMVjER&coz}qP0Qi%lu-(=3^ z93I>UMhZ3XQ!b<1>HZ~nQkh>zf+>%nB4oUQWb?AD0#s|RUUMn?`iaXB5vfiF}te zm`m778JuqnT7@}p4rA&-Q*glA*OIHt28+J!YYG;Y4HkXd&y>{;*;KRLF#C%V=Helu ztFuft-CQu3%d0s+rd^Utp0Z6cH58#7nQ8g36lNMEGc6g0ow;ESHU+l}hn>gX8zOTq zH3t2FJXSHsg{H_t{%B`F^EgBDxNt~y9jcgVa%nUd4HF#Ck{r()NG z#D=0f$|WBQjPRCJNHJOc^Rhcw%^t+*s=lYHlHKu~B))hEbj1zx`;yAiA@#NJz^JfG zM8zGl;zhY&;sw#Lq1(yO4#@&!t1f>{r-A*#?Cz%iW6RT_u`ogntT_pJfYkEKvu!{RMUon&bYKm}Cnj z$61z|WTLVG)(G;rOmaNX5*({O&K7Cbenc)GvceX~Y2lN+ zEH?&CWCuLYw#|}NkUaNKx{5Mc#o{5G(Vi)?hD(OP%cMC~B1;GJkexYQU|9bK-$!aQ zL=E=-XN|#E5}#>tg*F!q*}`dgp(W~FGVEav@@z?H`B08lx$KLDqGKv#%Ce#2-kxJU z4I0+|oS|agXf!lz2T9MBHC#F@$_~3ocH-r{u~c}GoGA*c#OlY#gYipnZyDpj$j77u zQ(wDs%c`}1-?_HP#+9qq(hS*2G&?@Z=`^q4NGQfI0+ue(@`QOybY0>I8v3QCdWAS+ zB6HQPTthcS$d5+{a^hheH_TEyt;oKz$nJx`<#w=WJdyOG5@LwErpSpDJ8NcFtZ9i< zK-1>YH5F?L?HxshDCR^WvmfVL=5G*<$&(hin8c6QNE z17%nIG-6jZ&WB87!c0SV77x7!WY_KHzXr@4yvjH0m@jV%HDjxC&vGzTQ1dtk<<_us&{JX- zU{E5p4*J#JSY!_HRx6i!Uy2-xLx5vLbWmjrTpgC=6<|M5P!#rzo4@}R@*=O#7(f(b&7JqY!5mq#~ zcgYdy;eF;!){XWa-){e|l$O-yKteXoy+aiFr)OsDGh3`?yD~GR`QXF8L6rXhKB_YF zbv=CG<_msLtVh%0Bd+vhv^HcqT2{~J$}Gu`Xck%0H26TkYYh8kdgiPXyXIz;6N`ay z9;ncU`U;AtaZ;_mcFR`dd^A-?)YNXM-CApKx4B})Y8+%^wz`$r*>nG=qG<4S1s58F zn;=;bkBn_wHW_?=%LqITaHMRdP}`L;^>rI?P>&afNwNb@)TO@%5M3pRuid(`+Ss&l zWnH~dy@MWc>&0ivo&07o>sWWOsL)VMH zq6{_j7Rs?{5yomaPlPw#{(AM0DYf*O2`K2b|hjE|TGJa1WJ)NZe@ zg0EzIV!sVnL>vu&96V0YtY zjHjGhI8%$>mu#r1yozrw;FIdP)$6v3dAMQo7977d*yt6q35hNf>Ws>tck~#$Oc(f$0 zArxA3$8p_YF-1F*-Qa z6K2MrLLre)Cw=LB)D`o#<61EC02;}5un*W?@--hnT?RoT3&s_GqZ{Q(y9i+~y*x0? z@}x(o=~W}_rRT$^|33_)R%rZz8myL^f|`tc$6BoK)wx#O|oa zmPU9PIbUSa?Oef)6Q}g`1i2HH4tUGknbN z!@^2>1%z0amlGm=DL=SMgOkbT8;uFHg!7Z4UGc#eXz6ci#sq|=TX^9flMV1vTU4qJ|{!YPc%uUkCQ*gS17ZZZ+0tJ_;_%#Y{ zRdA<*cPaQq1;3-<4+%l%B?Vtm@oy=Zg&TzRe?q|v6vt56;WFSryx8M#~*o`5;`G^D1qiS%Ej-YPf0oNC9c8^YJ0X*QQ zp4ZZ47(1aO*z6i*jnL(f#=Uw|N2ostoy&}}Tio`Qx4Wowa8}(<+<=kYJsY%hpl79Z zsi=Q;A9*rvPw4E0j9uQRrsnLT@VzdSxNdF`eePRlN80$Dym03!U5I@zkqpb&vOLbxr8j+Qs`{ z4~DH6^z2-#__o)rQ2(whj8Sd~<5c!^cTNN1(Kk+UtLU$LLEpqULO0L78#E2*X|gT8 zaFa{Dd4_RUp${6}x!^4ngD$<&n{IOUg$_9j`a<6i`Y_qu^FY@Jh$|%0|b<61AJrQvh`aGk!@$~@s z;kc%>M&qA>9;wk5?Z*E8t)Q4IJHa3Hn?sFmJI5b$gJ&$-Q5WZquWTpUaNtdr=bXuC zYj(5Rj=DF3PN3}5ZdTokm`B;&fwmi6KgJ^+x_3U*vlD%if$@UA9_B30G+>ObMO-L$ zz-?=%&iL}~y*TrL^A3G8+GX3R{|Y^J@RuCRmw=~(F3P^ede#H~s_0`mzFHTLbGe>* z&}PgpcP;avPM?$7MY`n8C`;1Cy!PJ@S}a$Kycw7WAF8ww0tOxJac9U}ex`nux%{r;434PhVsT*4;j2;}IUZOhJ;4>D- zu`LW{u)lflB0bAt|MiESFX!x1#swK?A`V+VWM6`QKj4QBelTQ6J#^d;gAJ?&95>D} zai6n&&Q5U;v`@j?sWDY5H3rVa;+NJ_sWI47J2Jhej$?+_aXdroIA-?LaqQ#KKuq^)AmaF_SI4n$ zQf-lHh5eLTp>itSf2dh3bQkgX@f*b$x&i4XHy+T1EEaC3vlFU^qLwg5F(+9AG$E7p zF}_LaIQQq_5NeBBLy}&use`FhB2S?O*^@?6&k`Du7i4ISNKJYKNK?a9kv4{)A()xC zd{|uNn-yUMKU1gy@_RD$BJmajg~svx%U!=O)%O^~e&6i?0l$u12x8EboGP=d1x7u| zE12~Tne-@M zA@>mW7g)jh-$O8MEa?6Y0q8ac#{ybY7z6dVU@Nhy67vPwPHURP0>MV6O=lg@=?X4n z+Kd{3g@V5#@tNe^f^%%Yk8k$&Gej>w7a9FN5=mc%Tz^3%bsK}?MMCQ~27g0xTDLJs z6(?(vtSS(!CZ=^8gRc?Ox{X1$FrnLshcmECM)2>boFG+S61t7Sfh?zW8-tfHP3txW z#U@JYHU_coBy}5uR18h%HU_^&tXkGk8l>iqwMt@>g5p)DMq-nLXOc>-#L9vt%(<2w z0NuvmCkWSFfxkkxF@quIJYyFA3^y{a0lq7V+h1k=^;1AGoi5EW5ruAJrqpd@ZVuiE z!h~*Pu!aoOk*qyxj}`nDQ|liV=w3^XSVFfk6B3@JZex%igRQGLxYTV7rV+hbqIdJY z2fCdjO5MibE)w1$>$%?w77|V9HU@>c<%Diya5u{)bQ^=)i6(R#gWTa+jcg)y8}ZBo z+9WAIYzLoYYO_R-*g-m4uy%==Hmcpul)8<44HUYK8T7@=pXyV;kJHKT<389PO~QLx zK%md#c<7klMaaC`WRrLuKr!>O&<}@Ok`5^ht5e$_(G&93I@4WWT|R!31N5@ZHut8zoFE$n0#68a(g{UJ6XnjiVTPI!$@KhTb4lKjX$y?uzb3&Km>*h&EI@+yvzHXn8ItIt+@G=FOj&SA?)4sQ zsZf=gC2Lp+IZPUvT5X!7!>?$XR*hnC1be1d=AVZTQlMoClL!A#C_v#&@ieucFzo0W z+;zV?h@-SuO}j~PEcQn3c=FBOs5rDF>D z|1oC*^mUr-4UgDa<>MmurlQFBnbdjNR$hD)cqxg@S!2%$gze$0?fK<)cDen1qul=H z4Xf>+8G)q-or?;{V9`Z0iz;V9E5_cqdd{QwlljQnCMW^fTcEH6y{mG2d-;E(^u0eS zDJX~(<9*u1Wc*Ma8BY5T)RCE$Ig8&mc`*;aTZW0?;;fDV_R^WD%nZxAkR)b3pCWVc z6WwkM$K!&oz>{CNXXAFk_vdY^pf&9YFTG&J&~WmtuM-qSgsMT^$EPWh)YwP}`neD5r3ung<0Dx{WQ8H_+M2Ks$ZIVR zso!yeW(U8LoKltJZ_wbNq@7wiNlgyKNFhXGcurfl<73cJ5O+0oO~}IsD1~^Y3G_2I z)^6Imf;t!{DtK(t`anX@gE})S`1qhU&wr*Zgvkr$R@t7^CG2ty4a zT;ezrt{h)jJjR$pxyDNRUWdv)f_@~Qy>R;6*+z;LFpZ-69UG~WRYc3;0VGd6s_94k#oo>`gnCMyz7!)qI&CEJP+9t*GcZExK=Y? z0Kd@+*GpI!tD*N|US8wyWBya+dg*Niy)wj+9^V`^y?TVb^bUYtCF1m@(>5c*OYg9UKRzclJ-%4zGNkbf&e6ZV1811y%!@Xdvh}0@&NTkMrXYa0 z#^dgtq!B<(?_q>dyiO;rN4{Yi@#N#K+Ea~jBl11_0r^PRGwjCO*@n=Ig@{<{s7s#ycxGq){9IFXEWwmf;+LQBPq zLtyQ>Y@@&Mo6IQgaj2t!Zc1FF7EWNYU%5``i|P0#ow$hOyLDopM|v}X>wO+^yjdjn zbi|W_8eyqB`CL-^2QotGH`HxYTEJps=h^akp5wiqOGi*2ZF?V6a4D*!+&&NOrTo2; z5b_-Q!X>^O6Nva$1zQOb&$YSakv)c?o)W1 zg5rEO;$KiW52%s;pB3a=F!3A(&r(qMOh)_+g)dZam4aIo+^Jxzg5OuLQ^D5=QP*1v z@?b0L`>TTKm`|d91&bBDK*6~RUaH_mLeLY=!T?){V-kK(rPFyF>BSY~UV%9EV+gYq z9I0TTf;?KrbRowFTtXc5FIBKwrPnFASHY-)4-q1Nn}Xj~@!wJK#|plq;2#N*|1S#K zxS?1-j}tN;w_Fc9cas%fMu_w|3K#ba@H&NWRq5jX#2*bqDqhHeS8v<2N(q8bWfyOJ zX{jT`z~NKNfy1e&1dmeQ<2ZF8vMe}ccN_QxA%SuMc#S< zxewsC2S4sP*p?dnmg2{DW1L*aPPs3(B#I;`d@}pEVkARU=|_DZ;8*rskW4M_{MWWo_jHqM&pk!)hYo9 z_^mR6RErBWoDa)1YK0m0OU&)d;h*LC_-l;-4HV?sY6kodBFdbOKPe2Qa78y#eP734 zNI|$SOY4V>RK7I7B=R97dA=;jZISs!L7_5ToH>1f<;{5+)Cx4e%#vp18GmHlgDM4Q z@g8Dz=5kb%gAAG10qax6sELR8*N?>rS0mCaLr6Y89?yOMqGudG+J}U7n^tYuR#Pjy z0m@y9SVqKG!jqkh6dtc|7!r$c;xpklOtZi#E|_Bv4J^e`ymjKj_d)xLBG^^nhcztc zJirk6`#7fVy;+GQ@eU49GOWaOJ>eZWD=D#N=zaVz@Mb6F&@CcnG8%I zxau>ui97(_z_jrlw?Z4d`dV)XkIc39U98sok zI{iuP13m+(jGNZT{{d2x?d^}$@#L|W-$Y(PzJ})`1^`$3l?{6Am z+z4gJ#CKUt`oy~|_twPaDh6qWN&^s=rpGnf`)*tXdI7|d9?LQx@rZ(&-aEj(7wh6h zzIB*5^R+XeE}b?LxDGR^4G0BF`qIIvi16~a7W^GY9QotcLi4v6VO<76BZkFbmnT98 z=OfKauMC?I-Co9PdaDuEWe_yhA|cFxx|okwHGW=tvp|pgD)N_(pQd*;!d`lNkifpz zmrkokgqPk@4}SBXN;<2#U-UX_QxK1kF2-GwmAIQa4EK*IT&2yh1Z z`@Dhx;!4Maoum;!UF53>W3JTcq-n@^I^Q%_BH!b>kcS@WVn`F$!`M}nA#xyM@aier z!G333^&Cae_#*~^`we|*{xq(kH~w$`rs3CQ1hf-(v5dCGOigC7!6nBsc_qI}@Sf+n z61FROo#;!9>C+P#yxg<>oO#L1KA|>_eDXS-Zy@@c2j@#)_Hgp00Ussg$<f~nkoP6&U9aGc3brcvn1as|g8%0g{IQDvvx3wdARS>f0~2>8 zapccpIwnPqDks{DKN_Nl$S1me!{#+5r}?f{GEtc@KaF>_H#oSsl{h9Gu>t(rMg2DF z-iD!P2c0^+bB$+%y`x{^$Mo&pD~S8|?3$kEw_Q@wQTZ>*@O_p@IcvGX2(r zHzev2h=7M#h#L!2~wZhj=ufbdS!C;Tj?b zi+4){ek))kWlaeJAG|l$qsjE--aN5)P3)}`8|;&8!2kY@w(5or>o!7oo~TZ-|Mzac z|9`mi{-;V6Kb{>ol#(#5Fl`f0Yp#3VDdz*uYXpAGr}q`h8R@y0#_KHn0{EROAD@A` z41(f32=|Bj(rM=-LWh~gX9@2ded)Aw5aHe9(HCDE;_!Ge{P?jQr1DVBf?AXk3d+S^t^j+J}Y$@1dS@(++haPrTcFga4$WIZ1_AQJ;rPPHY4n%*8qBa zp6g4etw)5HUI_Fwf86lVJwbrPFQz?xmOG;g9J! z7;%n#@;ddNmpzfZxChoT`G`)+-PF=Awzl$VCH7>vvG}|@shkUmoV%1mj8t%pf{ub` zD>y^JN(GlF$oq@sYZcs}V7-FZDA=grUP5$xtAbxs@sBH<^Wb#-cio zr<)q#OQ?)9X6jU_+;y7VUH^Q6|&rbmst`0XPdV18};42i-E`W_PL6 z;a(E%aF>NT+)LX#+~u(j_p-7M_wvdPcSTi)TUFQLu59RVtD_yDchIdte%dmx1)K%A zsvRW{b-G!Vo$ipT zPIqu!r#q;j)6I!?y2D#L-7=IL>U6r30doN-0geJJ1IaIXk;xa*A$cSC!JyD`?`ZYt|=H&=GJbyXeim31BNmWB?uKHA}KZS8QkJ00#w z&?mozfFr`4ZaCEGo@I2pXSR2`qhp)cw9_qW?Q|WK zD{wmTyTu(3m=9P2ml#b)~XKowz>}YKtqRn zd$hy7qqW1m%i(x+x}O1DT6Rm90eM?SG0v$OO|c6@$8d(JDK?5l!|^MTHkh&(&VczhEJPU(a5fR$0Zz3gTl4-aBZdTY3`sM{)Q?!q(bIe*`q zW0OYU+!gL``0xS0I2T$8Tl=8(9%OQdVW-|iTX+tV^+7wK``7CHmMvs-@cjVDw2MK5 z>x0OLH3eSv+EF^&J0Zo?#q`a0+<& z#b0Hecp71su%tTz_;9g2XNY#e2Z-@3>OSKMC*qx0w^7&N!Z(ZX?|^oRKFUI!CfbH^ zO`L~4Znn6C(FeIW56k$h1n(HYXKp+4;+$o_$?(Zg4Bt$} z4X@KTnF%>C^Vt|{tm%*V;Il){1KD=%Pe+VrSf6dY)rBvrUy3;LUDn{z$H6-@q)#9_ zWTFq|b-&0n$94E_QH44iqVYeQM)2z4m@7uIEk+rhC!wf$1$ibdjBggsK8EHsxJHzD zji8x6pc{H0V*CuuDSQX%t_E%*o%u1|!4&Y8YDiyIaId8n1+33>jY;_v1NR zig=@Je%F5x(uW%O%Lv_x`vyLWj3_^o;EXQjc8>0|CEd<_&1#HrzL!3FaDEeGX5+h2 z|5EtqVL#GWPoAO6i2FbOS=8~+#CzR`&b$|EQG@#c!hvvudq40n@F?&M;CBNL0lyP? z7I3UH-MJX!9K;#ncAVeky%vqX$n&N%q`xKd{tkE#IpiJZx{I3!ybWBhM_ITZ<$O6H zp3fEGS3&EjTkgDyd*-M+8@L6W=SycH&PLqy&@JwT2>TG8iF7~GX8=zD9>96j=?JGH zT!ypI(-00IJQexUkZ%g`AaI_4EkigR;mOE%0m2ywpO1WJAm4ewGl7SYZ!*Gt5T1m5 z=OP?J_#EWxi+rB|-Vb;d@=ZdxKf9!8$d`+71Yw>{AA+!h@L=RCM!rG7OMsUm zUk<|K5#~AfY=lb@9*BGskZ%C+iNGfzUlzh=BOF4${s>P(xF7O;0{Qv^KL_|^>P`bL*57ul zfyOOeoWsLo(eCNuj56&f9CN3KpL8t;&v8DZfzJf)1C0xTAD;Y5{PEI`_{G4F!k^7W zsKbn1-u0dE^O)Z|yDU7@fBF9V%S>Zi_anGRru13FGx&q`UfBSzI2#U=V`BXt?8EtM z`CM7hoeF<6KJJ^b?q@uCAhCBYLmu0xjJJT^#aM4>qv2YF^?kPk&n>P$t!n)VBJbD! zDxZmZO?~jM?+^Ywp3}`G_u}4!f6NihZYI`sU53y7uOLqpdn4ZCW;AM}Oz;7ek@q(0 z<+=gqBMtg!*l%flV;55D!_AMGHB)?pM!SifCHs7%9)l|GQ6%4DOfXN3ss{H~ zq?4z7gMB1<;j>QEKM%GSP$u@jd|p-3&P5#0u)Zy5Zy3DV#mzC=SD21D5dyw*L}QHY zXPf7bipDSM)7px4KuOe)5dE-dxn-?xsoFo{c{~{70ohA5@h&XJ3(rK+zbJz}p?F50^B1{K zb>L%&d94QYIriY#BR-M?eoSWy?v(}I$a7bg^E1px+G}|l zwlu!ydTqqeZUuBSf~*7k;+s;?9`Y-E<8lpZOTLd7Rrn5pcNq)&z9NiA80(y`ZiLJH zdYteLS@>?u4IdQo+~?{v?o-1^XIt4%Y^x3apT=0FqF(xB_0nQ~yhkPS!*3|;U3?1f zZ|GONyLo;0V)_QzSC23c_||QpjTl`&`oWJruph&`VW055iGA{)D3gPHqMxCsiSJ}@ zIhg-{#HzR_gnI)}(_{Z=dgwRU`yicU-+mFaynU$icHuom(-3xRz@tw475IU_vA5_u zcvlGH=7totT}bL$Se4S-lz#C+rU zkq`ETey5}z5P2Ujz7mbJd@>$mx_Z{yW;cK|t3`P6n zUb6MQJrVQ@u&xO{DjP(9>NLtu8PBzq>rw&s%6K0{8w=lLUE@q7G`m-zew2mHp<{9KNk5Q?8%!FRWz8|vDQLa@{`+HTYib$p*}N$&ZhriB zcc0TB<`(b!%y3lro@_+lw}oyK{m(k}J7d|pEm!k0tME&&Xc`VMl$OGqa zTN(HXFYKZpP0SM$`z!c6ZSjLYw7asqo$EE~eH<`zlJx7nD~9@_^gaAC=KBJ!MR&D@ z@4>uS+>P|(w z=|fWALu?1X*YLSxA&>A;sou}n|DVM?I#LT>3ZGbBkrWTE3Zrx>B6RVkg`(p}=XGzQ_Sf};{+jyURZdg;2>!VSJelub=`XG2* z*q@T$4_Gc3u8Cn^g!N^&*t7Bb3FD7n+2UqJTZ9kX5A?gJbuW6`8d8cF!qyN(pIQ+; zRVs`Jlo6qH=_oO+r-`AUwkedZZ3<;DzxJ|thSxu1CY;?RQu^e>WKS++X`!Ari25cq z3h3U#&fof=B6wJ& z*`i!&wg}dQq}ifj-b%yxbTvFl&@jSEEd}HtA7)l%Ovt0jGEx;#mXb#GR9(sIsn{|) znQ9nk1{Lr8B(w(45X`Xc@Rxha9q!I+IAK8fV z+w+h)W%Lvjux>>HY#T|f8+!n-lykA>8rFRLf!56-P=eOYNR)uq%^y(S=T~WdpM40? z_DjrfpNFEp%SF+jqhffXycI=#8#f~{#rGAG97C=G_AXN1&D!lwX897n*BbU-mj5bq z+b=P;O=O_CQQy}^@H%FAM%48_EBHQRQ+%l=1mGkO_MnjhY;x-0wE4)9I^+pNTceD$ z`;n5Gdstvr+A)?GiWLWGzO-Kx8}@sF1=6lSC8@(-5?C+|9|w)pa7`i&VzRwA{4mOe>IS;k4Q0vrwkxrCr3d zBAJ$-_BEzOBvz322~u$+=A`kcacVJJY+()uvk_oY`c?P?r!bR&2dwkbwj-N$?sb9@ zBh622@(lv>8E4Q(r*$5SW>({mY0c^L8KfC~@GZbF`}`JhmSH~uF045hlHeckhxS+V ztkr3|nYxDM*ulb((%Lgff0Qo-?-Yi$ZkoUXp*8RpVqLLGU>PACg@g&E+XNO0)z@BW zSQ{P|SXSCja<)-ejzzbPMF(4(B=5PQ)is8-`Av~AZ1hbLRcBiMH>{z!_+QjF6@W!BluO6EOTMJ{hGTbYS% z>Yd3;@6*i0rg}0dE@>^PFPoZ70+&=+z>pbM9UG9b7~_y*wWigumu_T9j>9x!H%ZKw zwi6kxn!0v9vyGYkpXlQ-SD!(kWcJy|%&wZux(_Ogam5}TCvU1Bv7o1FF_ z%hgJ(3|mEDYuN!XgqF5}*t#{qgdwybL(D1Gli`rc0H2t|ZF-CFuctGltOrx25r$Vx z|Hcpvq4_s4HwW)>4$x-OvEk%3?E+R(C(%8Yn1TL!&Om$AUQ3SHR+-vrg;v)Y{%sOF zV5LENY51?=;L;FU+9O1-mgwD9+F-VGJ4e)pA+)qO(H*kv{Z`s>a&e7BAF$H0nEFYH zK4hhRmwkJkMBA*i7dZ)TkmzA6jV|^4jcg(fp{13w*-bL_VLL6xvdt1bVy88;qFrL9 zjcT_;G=%2g{iG;+97p0GMTkGurzt*8rxYI#w%U)do;{7|w*kQbl;BX0Im#6GKQbB9 z@iQ+QSW^v4S0hd4Fe|e&P`jCD4j{Gwgy3M}w~!a!i-Fbjq4{Vy7Wpg1crb5ZuEnN?wx zgTIaP2axS;{Dyo7$dDi6PcATHD6K!^SRZ*K;bCt8ZzTWE*`_(XiOv0k?nC;85SJ;rd(Q~>nUy}W>nEX8`nhXAZfRY+pVhq>|8bxIJOSZ_t z@(aO@rtw8vbjYF{&Rg>VQM0*_6u&IP^RtB$%?E9cnR&US{}q+b7;zbPWaj;>a}$Fk z%`cPYWh1Atsq1;en@dJ=X)-skS>~dVZAoPD$Wd(I##JPo6LCmn3q9)DJlFBINfX-V}6CGI!3{y zDx!`e$^d~@M)9r1-rtU>eu!cp4lP2IwSTfPCJ!;k7*i%{|orKG7}+sZg4<<$WkEV zYVgOb%%!>0p>+%|8hWjW8oGyoYHle$xtgp)!BpOG1&<*V=Da-Hl=s_kF)d34GF;5^ z33wgFZ7inpM3V&y#cZ8qGHW5XB<3gN!j|GA(P89`&a}Ct`lBV#Xw5exqW}O~kyTV&*1deyd_uBx2rGF;^yH zey3uZ5;5fk7 z`q#^_SVCktyrIM}Z;-<};AK#Inbh#bU=|=3L5gDZ6Re*pma2vF36>>iQf2mooEif} z33HJ#AdC{XaRR(9sgOd0hl;D7gE?Y7eNAjv&3TgM87Wc|9J33Gtg0HvuLSXIB8Ymn9Yc!Ny!A{<0aYJJ+$>s~bd6mGd z{khrW87ooq5@8keYKv^(Nd-o?izl_YXpnFwuCT>}gi~pSEzRb8pw4;ep!Imb@37c% ztTWjUvVqrFoET>1nNJb?zJ;zk8cKT9FkaGJE^8>2 z^{E;r$Ql;N8qQACa1&~n$+|C-HIO<5hPsAjJ!-gA)-X?Uy9IXNBssKg!w{iD~dBISnf1 z(Emve;bOyiob%Do{+uiKJ>-fXiu5F%qxL=7H5}H9#1dg%QizAV#*Bo#2F~Z31fD&sQOYu|1H=E216&`$ zb@fL(`Q$b0@b^*B&bY;4vLbPgtfyz}gwJery0#2~LFO?>*oZ*SCd^2n%j>tU+=^JF z!>D2HmQ6ATUT07k+mFv%=D3o=;>bl~$5+Ey@^Q0#9_+MEu!j`cYv|fKVn1QuGqU#O{w$?g^Y{e|<+_@|?MkwfxS0&M2y| z-@ksfofnvlzG$cK>#NJ{r_9ERxfN)A1zc1+g%uS~jIl=)HM0xgsr(vvrXJ^%fG?O5 zE}v*$R&M8)?}SV12pV~D#hePesu7;ki(oh;QbFUrup9jN_4nAX!NoTE?Ate#!zp=W z_m~NR(Xc3T{|)F&dvdv5xLS2;NnmNo*o!f5Dk>uO^XBSt#pCBj)<>$}D~P~mFfu=A zm(Lwn+_WY#$NtH6<@OoncD*o8EIN~23UkJA*`85e42R(3*NiP`YQ716%}YyY#JIG` z&T0f3mlfF;6xk188o*d_U{poL9yl`}58v8$H3kA9_&~Qy1L4PK(qeI;J)+zm_-;D_ z^{efx0uMz#S4*?WCyw@q5?aYnPtTC!?xwmj!NpS@JTfmlxS*HdcJLq7b)4MWHj>8HbUWFvbz?%_DGP4`1XM zd)#$P9s&Qg?80Mk(hl>@#bd^!?#9}?Gegl1(1_L44CjgJ}c>eoNwWL z`JA4aOaJA2?A^Xy{+5*H)XGf0gC%T9a+5mQOJh!ENVkDo&+SOlZP4)zh%fWR=ON6i z9q}(>`8%19am_ckP7TNpzTq)lx=z=$x#{8NdAjO19nY-_KcwjRFv7gJ-Qdd{+BqjP zzh_g!B8S|LghfA|=wp2f@`x4N=nqQ#RI)5E~ewc>XGKjvZA>x*Hg9mekne$3N`pS~Do z+AH||0zc+Cj-S35W*W_XW#Gp=RIyB42s4e&AxrUNo_zfD#W2$r;z!4k%u|VpHAYn4Xg@hpt$qht6>kuYo5(5MT1Vcth2}vYD zpwA&DOj>9`l&W=zt@c@K`=gXvtF*| zs(pR^u;FIywTHFmefDtH_uyv??ex0~zx(iG7#<`ULp%MR!S5yf80Ja*jG>)=AL93C z{21l~{EVU9`0;ef7<1Gy34P~=K>ztb7N%jWekbAg5%>_OG4c&_f|Scg#}%{#Z>bCz zHywD&y^65TgsCpQV0wwAKkzNzc2{xa3+-`w@#-u0=F{uuL+OlaO`B3mSyto5x(chN zt{yN~378!SF{Nbw^yzWKat#2g6_&U6UHap<*A~!zOf48YXHB+ z>?^SEn58k+2?ZX+180l|Yx2~RnHSB~1ub4!qSfq`u6)0N;^ssYLW}B@w-9{`9GH zFG2@i*SM;5O;uBws#5R6_p2(apxIx)QJ`fi(R-@vHB(kmQPqf#*#mgJk-1WXr@*s~ z^K9ciTcK53hLF|xyg{XI+4E=4Ur=HxBy^Szh-Ygn8Z5LWGOI=xI^Epq7h8dTf!znb z`|Bh1*VeDs$nNY)#LHLlrcIwSy<~a-p@Ob6=bnF&rShrZ^64~V+I$~7;^g*K0q{5M zrr1YV;0o2Qu%PBOI6bQpko&cjW*n&Q#0&A@qqU-}VpWxD9Lxaax~kQ*PzH(hb?74N z+2@F60^#Mgt5w(;C6lLM7AaX$JROOxtf{aTOuyj5>2pg0A7D6g*ix2$daJruqX8A~ z4QR%f#_KI|qAtk!>w!MBvL zPcg>$&4oiy-lP&;F3My1j67b*FwQvqXcohQ)v;$7j{cVkH6E1+chDpTM~0(WgI_S+ zBBa|6H>S&#lu5S*_CRzCugF=`;bx5Sy9y3LdC`S_Ik*Bd^7g=P;!s8nAei$enE>E6OJ^HZR^9yxUEB_vHK6jkM+m$vrdBY za&V5l-w1(UzzWVcLHFb1galJydQ9CnN8*B*(XYd}=(+5>G}G`Sb{_NYn_zAzzq`Vn zVOX9CFg)wB$xGI@hJZKc07f2pZER~4Ean`*#J>l7dgFfvEM|{S|03{aFHL?9c(dmw zkM5>!a+*QOUkYmW*yLA&H|GN6Sw70w##ZCdoCDCGdEXoeK>mvf{H_H4)&%}v6ZrcQ_rH{)ZFz|4iV2m%#rqfk%>_p?tnpY7BY4^=l0ID0p-J!~Bj(@Sl*tPfy?% zCGg7=_^JfHK7n7C!1HYyW0=34;Ca!*d@_G`B>3N#z^m1iEMH6UvSm(La8E!mNwX6O zYWORX5hOU@{tZK9xJVC@l-fM zrKL4`V-8VqP}RXDy8p3{QtUHv0#;tLvY`yGdkL#HIcmd*ZGs*q*n z^=rBAM@)R;t!drbI$>}#tQLz2yUuWGSxH&WNsUtVR!(9T(SI6R5zIxCyn3ZOZ7 zUSVF$8iw&i3jY;CIiK=n^zGz_Bl zcG3|zE)$-gt6{ulq+^k9QiR(ebd%7Vgz~xL;DK`=se>asq5FkCAoPIHgF+t@`XnjZ z=|fQFV;UAbY_D2Ur27%+1gr-z(6g|Pd6IMz%1esZA;NeZ(0!87O475S4@e57N*7J7-$a-lUs8-%VG+ANgMOv>LQ^fsa25&C_h2ZjDj=qo}yguXBI z_d@yXp-y!$SW}=$k?TmZIeR zL1@&`JYS}ye7>4Sx=83Up%p^aC#K=g_X!zpn^5(w8}M%kk6R_>9}zl8=oq1A37sKy zqfpLglzY992%-DAYm6r+Y-`AfaP~o+WgK&<#Sj z3cX$Ew}l=M`ZJ;L3jMv%WX#7*FH>la(D6d22%RT%h0uDTR|(xA^hu$=5K8a{&PVSE z{j<17G5<5&F{JP(b|UFSai1=9fzYdj?j%LH8-#wH6btYJ;{Sxu7li(Y&_9qO-d}`< zx`9XfgM=0cJ%<$GCJQCDC&Mig+932Qq1Ow&l@#&Xg#N3z^9qOY9~Sy^p}!RRp3vV5 z?Sp3n!$*aV6?&G?=|blVT|$agk)pe_QAe#GO|$^#6CEzZIH< z=Q7><3C$JyIiVK{Efsp1&{m;03jJ51KNR{ip>GQPjnI!tQ63l1chXFu1B8wd%Jl*L zCzB$7jijizYlQ9)db7~Gg??A)lR{q*`j*f?2u;QMfbvcknlE&+&;>%PgsvBQtCP9rLg-3T$XPFZtMFTe-Yf182z^56YeG9o5&zdh)3^>o`S{6n(vdyM_$!2O7QRh* zs!C8E9&;9-sfMB(D0j7&P8vThN^|^gu=OuQB3NQ;qA^!L04~Jd2H+4t zcYZ4{ctQtvDVzc~R@<&H0%ibHVTH!MAl^ihwW-|#egJjPLs4Q2Zau_sp-6dq_6Ep^ z0SBOeTgUf{T04dyt`4v96irwa4zfbA^7f=iOM5bKjlN&h(y_PU@s9mP4|nW~J*@C> zJ`n5*V)#Xhc6GM2OJ1VHKLD1&L1MiuIIM6R+uAy;X5b4zhkg>eAF&WZF@%fG?{y&h zoV~C8#SS|*=a3VvYj;LMA0ImWLl?JoU|HXNdu&tte&B~VkxlKkwYfb#TCQ~Lo#+4xi{W>$sMr2jQ7>Zm;Tv`xz}b17 zdE6d_eB^g4%8c-^w{(P}EvgKYfGI^c;BX#ttP6lYIr-4O*p!Y*z+u>HJqBE+*HOo< zz@d4%6HjrJ)yKDE8~jml5yHeZL>p#Ywi9>-z_YQ|AI2)Vk6na1v)Vew>;(2z^NpQ& zoDWQn%|IFtE8L3x%@23%r5*SS3>T*S3k-hVg2O{lKW8h<17PZ`Xl>6&{7B@+&J=|0 zi*NNY9`j)XedkJhh7tqdYdcKi<_Y?pO>tmbHZP2kwo%)Im%P zOcMivThW;uoqA}0?6Ho$J0I)V7kx~n@e7pmYiFQuA-&$fcRCw+X#PBS^dI7y*lm*! zeFJ>*itDUbfSUc~VN zmP`jQW03D9hp~0+&U{%1+YV2i$3DHRjZ z_1HVM^$6MxZZS|`t69KZV>}l;aF`xsy%GCjFEDuSk3QRx0{>)SvRS};vw`)-Im=fda6Ev{GtyKTQCWIfcZ3i;2twxT#lDLo!e%i{vyroz@Y1}nO&cr|EuJR_EMpdWW^#yre2*zLe6Y(W2ccuOaq zzuotbd7&fJzN6E^PsPJrHR-ZFR9P9wc2k%&@+_JJd|nj>X%YLV0r^P-_Ktd1;<@tm zbM^uorWy6PJF{g}TgTV%<5@LR7rQ?yO6-WnT!$m%U zEz>^f;oY4rL)toW+BZLL1Je#CfrrzJoOV2qx<7Uv)7jL{aq14a^_^B{TDy&L*DLx+ zM-uYu4%z-rGUPCy4U%u*^ZkZ(p>T9Ct_m?HMxwhqS%<{&<9vX2!dTqV`2f-g0pBYF z7|;6I^GIiO=bp}P-^KhI`y%k7fw_nAmV^G!=aq}+-GV7?9rJ+4lLoie0+a{O^e}LR z+%~&Cgu3nzOeJ6vW4!Oim|1xETS$Zc2%m}Yy}Gvp{|NPy0ypHd57rvJrXfr^+74~> zv{P^+#%1GS;_@s zX{d-esTUuf7QF^|XV!!E&T9`1Mz|KpH)ShA+3e`#Lq*Y6lzB_%v`A~GjkXS!t2^4R z6?wDUt~>O!YK!ZnEp9|x%#yZfK|5@I=i2GO@WJ{*^^Y+60@g~|Gxm1iIozG|XfoPM zk0*_979#!_`XAdWY_)b`8;0j3(htlF%u7zhuV?q5-Tq8I`r6p-?T-uvF6hB$4x_!_ zBVL|vzk_Jk+pwN^p`#hk%l)zEJ08F@e=pV~tud@au=cT#ZV3I%0nX9=QN146hqc&# z*!NnG;#tww@g`*2QH&|{H7sG@BR1t=U~|EK9q#`+TB2nhvMe=66he-tg#<_8hJw4_3 zw1aDKtnIGb7g>aGz`nB3=h4s2yo&bin1#L{Dc*_x_%i&850T#=c^U06?=a@vTUukt z1DWBtkiavl>SsF2?8LUI_1@mt<7m5`o%@QO=-3~50%iPa=U$}A zy6qmTXuk$^9m4$1J;dH9+7NxJC5nB`&PO_4LcTaxq@q6f45)(A1FJhjX-|+_K3@5g_eJ}St$TR2C zF!t2Rc)qH=V&oCb*Dqnd#$K}d(GGsgf@KXyA5mqEAwL_gVYU*6i=9Uv0zu6)nTM32Va0HELhXy2(M^ z@5A`L2KjVj+b|ETfIputEhzKD9bDV7eIhH+2G%Q(SESR}(AE)Y-q9IrM*X3EQ_$z5 zd-1Ii^Z|P>{Mv5n?7w0M%C@7kNwovxPE~OotW!3^Z7b3Q#$NlrB0RS+=cmPP&fJA@ z%>3GzBeEBRHYnUjwBswPU6&%B6KOj<9P`|M!A|TBx4n_qJ4#TObI?B5M6Nqx4(X0L z1o-&ZV;;UGjDFycOU&cVZ5=mr{%G6TNeq32?U;e~|0e1*BlfC{|0gl7^ce~3foC5q z^CXr7@{(gdc4-0J?mxIoyP<4J#lU84-`<|i{&%plJxP^&Q#q>dosPf{wIBFdJtdnieGJYBOxETkzo>|6m)eAV0y)bsvzQBxK-CzB&Yx?s= zkiqYtq+#q5OR^AhCbjQ4#C(NO56qiYyyMqqPMC->cJ^MTb;)6zZ*`y4jJ}6Cko&X# z=vz3q=w5^}6Hk+v@$6@LEDtcA(SKAQWuIGvbfe5O&cBGaeShS7jJqc=CSOt5upF1J zg>h*W9ZHU(4_X|T@I#zdjOYCaAMGHPFY_0Q`Eyp5h3^3&y$rO`f#_@KxAP81i;#~M z!1hJ@#FKa6&v+~o-LfE)eJ0}|zBzz=@j1YCosG0pSU=Wno!l#_7f==}V|^+ZkU=oV za6I&5w;;A-cMTSq>Z3QNY4pY(7-m+wpf?JJWKUrQ%rOGcjy^<9>}4<{d&gr!_VM5g zQ%9tkuYzVIS;4P@X8PcXcumu1_$EWY0Hh%#_+J(^h>(KENJK~iL<&1F6;;^Vh7x%o zejFrX7V;24lssLsB#4rOBp1RC7U{yq<3$?u#-SQ>GTR4n%<*w4hiP2O;RcX$gjX*r zSJXio>E{S<8u%#!vc>zfDl$O$#?jb_B_czb${ibDV&j|_1fI;-z>~4=0GZ@F0tE)5 z60ZsS6=7N9gSliL3;k-wErY?RoRDYhx?}?}nDtAH5)1^G5X3`Q4qb%id2se*_&BY& z8V6d`ZrFhY^?9t8053Th-l1WW5zzSxug*Pq$^PI%8eWpVfS25hh=7;;2psSmuR#1z zlB4|qFIfpe8eY=*6}?iDLtmlP2jFgp-7>f(D}cU~pHqNyPB@o2Fo1{Q-!h9H;9)ln zc=$~OO%DA#{&r`q)IZ>lf(A*WE$wRjf&a(&+ao#jHi%1D!K8Cp!|7}BXB&P%sta$0 zU(#f{IN4TsF1ab13x%I1H&t`t@YyK0hg28lYY$1&@sLEk2tJ+*?tE1&g;bXedG1Rn zM$$#CuqKBnr03TV2RAZkhot%8pVJrGVmjdk6kw3*!VK*p)rHqE;YB)RTKE=*yjXLQ z@crZrQeAigLmH&I@Q<0rr8;C*_+^GPNOj>%rf!hx!lRg;L8=QEkt@~l#)JuH=^@pH z^O!=p_A3mJWC|6Un-qSWoI$D!Ph&iTR2N3#R?;d~0Fdg!Rpe@jN~)0RBD7ITugmdg zuoZa(`~;tOn<#5F-+bx$Kd|D_V-cyYH&v;U>KK}hw~~3QryL^Hh0lV;N2&{Nri?}l zGKf^CTg*eM>kXALAE}O-g+Quf;}WSZyq)ZN&ED#S_pyF9u#H?G)rDU~@;*{sxF>zT zpmTAL6TXs}@{sDn8|mvI)rE<`=p)sIzf87;MI=&PxI5WaE$RU`+`_mXQeF7Vl)Odt zwCsa!Z;e!!21hp=NOh59*!Yt=HAr=YAp|@FqHcQ@8BZZpTO?(97{>ut$`TfeRoOyr zspdlA$H-lxxo~(gVx%l%^8m5Yayb%HmY)vCK0n2udm+KD!XLbuho8MX14thgtUG&Q zhCz6$V0GbJbsUHjD~(F^&vwisFU?RmI-6OMwoYm*lNvCKzFSpd^O^2872@InE-J>pR=F=6 z$Vrn^SYLow-J}A{?+g7l%f49^YF^*U#wN?YMKRdtB!_xK^l3Y>9kp%P>>e6PhI#yu z*V_9_p-Sp_34s2TjqS-G7ELfT%cQKv2K|eCSR&8gj*VATg*~kx+S#T zF3~L^+$DoQ$HL6kB8rE+fht*yJhA#0Xh*6363vwiZSd-HiC_Igeus!VJ)h-L%js4Q zi#ZadYQk?=0hpnC5p4K5usOTJ!)Gdrl3OFz!ny$#yHq0!*(2M@UFx!fM)~5ky~4d6 zPPu&L&e-o{m@=13tDK6or;Z8J#N8ZBC8s|l0m9>P0&)%X=Y*hi-Czhuh=#dVn*J^>dKVh zC0h;WMOkXzd0B;Vc7+Btq7(hX<`6?q(o$cwRo;pR=P^a$O7aFF2_`cVb;WnjhEWfH z=#!|$;j5VX!r@IM^M*H*Ts(X`$&&n=K!#KO-d;9*ul6}WvUE80?(N0HscvtV41bH{ z(&6us%#Tsu9xG0moWF>*1aw$W6lowm$PGV@1)Mj07|DEu9l@|TG_>s799;(sJ@%Kn)NL9k6320tCnh0T{epM^)35O9exdxGIrG};;!0D+oKpK z;@ZKK#P##WR&WDlq`B6Jamf6BCi*2utq<*SP<6KK9cm0w%$Jod_hX9DcI&hcSmIw% z#0?s^j!nHP9lYtVy1AF==DuDvH@dHSzU{Wv1MZU3)dNm*=n87Ky-{_AL5=mug0|bs z20zB^?NKu!I!O)_KY&|`X-IF_U%?FPhUsb;-;W#m0rHeT1h)Lq{4)`MhEK*Hd&%$% zNlM^9ekp9T+0!=wi*X!#;N>oR;5e1N^)9nFPGwHpqX@Cld*s_7^b2x}oT2Wa5u~$r(dP77rUsC-qw2uINy{{xp0#Tmb(!d>#WV8or!l>2TH> z@C1i9Dw;9;YES~O4*#OEJZTO8it?E>{Oh0?`rjs5IQ)ks^M*gJJR|wfgThRBf|M9jTgOfEorPRZS069e1Q^1F@@`IZZ_#sfwSa?73=Q8>GTVRl5vUT{+5z ziE1k=Xo#}r?qVLYb!gS=a_pZYZ&B6j#14%a%N}NP0m{BMT+Jv^)z`E=_W)Z->?07} z*#CnKIl^XJ;QeX@a;)`8@ShVZ&H%lg9IQc2T}AD>%BuXvjg6SWp!BblkDwxk`Z_vZ zoCghuk-J*F|b`od~^w_ugp#1`y4()|Sy1;$K1j$}OV+ zl?K2zw?{RgxBxY#@UQZUW3h3gWBIW$bH>C9R_0$gd+XTI1*7u|#^$=IEf2-U%qGek zJlxW1_pi>ZS&Um<8_U1L&CYX6TV`(#-;h7HVD24ms05@%)4;x*w~ygbFLXX=j*wJSIu_6a`mjj)$Y36-PAl3H|Abe?MCtn#?F9!4AJI( z3G}YZTHIY%5=?G1Dv`NgpI2Nk-hJ3^$zL@NNM!i{9GktlU`8xfnD1_6h}kny%(`l~ zfAt)s_xarM`KwpX$OoQV?(O;E95*Y^{qt3`+-I-K3$JrC^V}cU)om-?eOJv|UA(de zb&WR3ALm|g&&t0eHYV3?wgF7{TL6cR8(k3I1v(au<34;PpyS++>}vOMtHu5D)wA4D zza?B+I2%}iNU?U#*vkCr?t!aY++ST;y*l^v`Sb3Wc}92{AlR@z3omvvTY%v=t}2Ec z#$w^!uhl~E>>rJexmDHf>3Qz=-P;SQ08%!)tqKXp#sIc0{}=9UKn*u|pqVfwz7mOO0o1Z_ruo8t_r@d!yA}Hao zS+m`ju4-v@U$<@#ug|}5ELyxk<$u+@vhn$^ZURKzgwX|KMgu2qrMskseZ~EqH4ADq zn8JZ=a#r-BbxoKSqfi!HTMe9)D92u7UNkzht^&G`jaAAaN{kFF&lH|O5W_8tgHz01 zpmF$XtJ3>N1AbA=3P)h9tw@jNNEmKnV;Jqz(xcNfKH0VIw$N6bLTpaigmtTt#SPs( zh=XthF^daiWO3V-p=9NJ$^N2qoqKI)Thi9#Eh(GBo4Ujupe|9!PmApo83(z%cQ|o`Ul7hFx=*($Bsv##OTcB=6?KVs zHz%Fyy4H65)P$(synhy@^c6;%5LfG+Diu&368O5R+!hD)Ww~d9Dk@ZEONr{FB5*&X z)KGmVu5XQIDi?lnI;PAv>rv+iSrKE7fXQ+pumL}A;&E=|nIhOL@oT`Z2|vOd*Wzc) zeXt+I?>YP!kE_^c@H6HOBv**?-iqHF_!;wOxJSW{!7qki4t_Xp_sm|{AH(k@{21>^ z{HU91jM>yuAF3HYW6ZuO0~Nydn7S5k*;KDp4sSN}*B6yfa z)uOeup`JP7^WRr~u4#m}a}&2vK4?{30TfCPRtkXbf+9Gzqq_nRQ{5UhR{>bBnw7w; zDr;O#P$aJY3`7`Vyx8k~fH3${WNkgrfz~PvB?SswzHEyVAU1_0h zl-hQMa#OHK#7@$JTUg@a;H^R9z{|t;%|Vi~9tyTPg*XBru^vHbDZ` z5>hRIxK&YGRkpT#V-s+l46>F+&tq6MJQ>>DS4=eMh*TK z2f$^Jx4OXGk{Jl_fv5rW6OMSD@F`TS&jMA8S6SGo;%6RHye14jd(9IWFL1=+BsHIn z=MEzFmG}&Y<$yIzc=MS32+t=x^F3qajfM-un{c5FFNm@*Pc$+7jGa8gJ**cCWIewr z0Xy9vN5-gEXpE6@E*!8#@k|5`ucGiQ2kXF;gNKPG4g=qXf^ofIjPaWbhoHQ7r|EK0 z9?NIs;XN89ZyYF1^C+x6v{n6OILf?CsB*-g{Rr1=5(96tum<*Ex*s6jHn=feEG@ir zTVM}Fw+hGTwJMyIF@9Hp3(DI$-7g0Yk34yMU^j6n;4#RH(jvzAT@OAe?X?zaI+)QRZD#IPZ9jG4g&22lTxL z;~pJz9Dbr)S@v5G65k=OCb2JThW9UB*KeP9w|1#qVO8@c%eq{n* zm%v{R-t18+uQ|bgYXbiz@Mgct@HZ#;-;u!Io50_nz;hfK!~8#P7*O(0<7f7@Oz-ss z|98Qgy)FHJpWtr?x&Y;+gExCzhVPr;pPj&u1J6@w?sXadyafN*34GkiqFH9=lv%R% z$ctGyfM^OXeSxW@(2x9Oo`z8p@TtI>O2bJhkf+j8fp^kCQ~vfyke~!7DzFRFC{%&% z2&@fj>nqf%bWlmdz( zFciKmQl(I$;sA-`R;cl?RE(q+mwIl^!UVSpZAjW5^-g+#bRf&=2RGS2;;{{hoKLW4o5uF5xAlT zCC1b$ao(&RIr=|xgNvA8KD%|-r5u{a?lIx&Fwej3x|XWFu&*z*g| zxe5whmcoKUIE4X)XV)A|oWv5^M4E>&Nm>B8q^Oq=;*f459jmdMNDJ{$BqmV|gMt2q zy+9$4SV?q`_SSp@=;>G!93+ng#EL$kh<^hq+)=FKV-;aCM^ji1D5_df6kSndT~R!~ z6rCcpSSVkLV7zjnH9{MNt{2)Y^ov4o63Tgq@%ITmC^QBAh3*{pq=SSK?~eRgLT3mi zt{vSgh4QT>@?R4A6`^+vRoFxDe^_{hJp}$u;T84}c!fO#n&N1AnL@LL76|2+ubJM3 zLN5_oBXqsc%|iL+BEvr_^aY{(;1Jz89!OKrH%a>n9WQi>(0M{v2(1@-mCzkRZxzbh zY07(0=u<*@9!B>*7-yuzg%%2(BD7d&sn9y1>qwDjg^dLIqPV{$?uUf_THOCf=s=8P zriT|t6+Mp>@uv#?g1B!H{yL$%#Qj@Be<<{)LSGj8fzZDSP2+e+dby<7J8+&Ly-?iC zNs)fF(ADDpFQf?9Ec_m!Un52MZwOV`Mv(uI_&+7|w?ZwXp|K36}A!RXmOt| zbb-(^p_mCogKNb3nxKnA4?!OZLkEBTFuR_Dvl+ZtilZNpa_=Eg0@aaqkfNUqb&PG!^qJ(;pyogwUBn7m`BG5}_-^y-w)O zLjRxme^2O-NKwB2-F5tIp(BLi-DM^B451g1A|K0z)(BlE^ctZ%gx)OlyFz~?^eLeV zvk3BjC%nVw1o+`XCkdTKit_Ojp`;gy`*NXo3f(XM4+wpR6y^J!_$v$|$e)XaIMZ1n zbTujD)(gE{+*^g>Q=cmQ?LzMp+6~Vs%E=HqL@2*&N%zI1kh@Ii7EP$==L z@O_026`CjXETQ}=C*?K?eNgBVLSGQNiSsnlMN*c7XCfYviZ2v?lJJX#=lYxBn}y#b z{9VG|Bm6VMKPUYA!haw45(Hk~Y)Bu2H_Xr~g zB-DP}+k-yN?@)p14;}tv`DY<+Bu1T|)_0=^?|`o2?sq?qI)~EWM*SS6vjTmTtOVJ| z)JMs&+;D2@x#+&3o>nSI60}Q17X#HtH)@Ea#4D0eBPFaAHYQJ`~sDbp%v(8J?a((TSi0D~Jm|nh00~JoJZ?N52 zp;ibs+`aG*%mDoCZ{v@{og7pbxM6|nLJ`9!IjXy)xI8I_BEm&*gp$H4goKj9JdzY= z2Z-kv3MFB|XE`rVW4oEDS0aR!N_7g`O8N%<`yIgFlq9$e!!PWP#Iy?M+*ELs*+Onq z4gNZKl{KxNQIkUq--9bPe9aku&b7~BESM;M_VRRmcEqyJVJy4Y>S^Cb%QQO*QiPwq zyvOy7#+S(LIX3NdW{uj*BLnF=lcjWaMIwC^r}96)*B*wrkm30SIXM5pZtofs;+76#=zZE+?`>XA}aY)O3mRBnIy3yZujpntkN+vFMrYhC_m3+Si*w$+-RLS)Drb0`V zw--_6Uf;nm&x-Xt>C(mh44l>HSk$MJtn+8ioo0kv|2Hpas%rBpPx?D2pU#&}y1s)6 zc5>78aZC|+rcS)+7~D7(F>bsw^ zdG}z=$oP~;YUGu}ZsNdd)xv}8MPp2PFNH%;9&xozJ@c7wmya?P_URXVO>bVG>d8auId*bVi5b(6} zcc)MgW$s2Ja6N5|iE=L-&;$*}{Y`|!(VAyWxL$BEeTtrcLbwOLP<|O1-U+ym#AGS(GePzP##U{?2beat#d`5gNZr##JqI2B z>;O-BMFE~|yCJ}PR#Z}xWsPMT$V?xzg zDg37kKS$^Sp-Y993H`j#bwXQ&a>dW|zAkj1(8q=TOy~x;TimA#e}T{i;(n>{i}L! z$55yg^#sO9B-*wced{_qM$D`}JZi(Bn{TkOx_>RYw}bdte+CW*F^Bp9%gwQgiL=?m zx*--4zJk+@G^kY(X+j+BMWB=q1X+; z0-1WqirxSm4Y%C|9-Lcr{W})0THJ<~cGgwMf*-IjRQ^$)nAf{oMQ}qH;Ob#yE+S^h zK&xnTX9#5qV(>9cD7LA$ZMF5WVw*D={rhs+V-HLt%7*5T|OEdBUSyAK_ zxFTWja7TJuJ0a&Vu;boC7d$q8M&P_6cuedFp>P8bf1;7$1AAm@V2{4TQAhp!Q0JxbP{n!#BNzW+^vTCZ=vFl9cp}z_b9D;CBg+q{KBq`2K^iLAMq!7Op zfKvd|3k(x^ANtbA!NW(bjX0Zc!GjH!!+23XL9IH50 z+lzYMiIDbg6?dUdWw%bH&pq_Hk#%Wb9AT1sn513O=OL1>@Y!J(NBAzQeUmN^%CHmJ zPYyAqjBl|d43$Y+rphFagkbO6kB7GQML%H(oL21t)%St=izfD;(tE(eMj&(n%l=!o z>()^sHOuc^z=%ge8=DbHV7QITJ^cFowj`nsyIRb}Nh`Q!5n^9u7tFY0KL zpx~d6cY`5eY(ZW@VZ+)gVJWoY(}|sLG`q}UP3c;#mvyw$B~L30k26o}>IEn}PtUuk zFn{cXl?N`uq0`veg?EgtaaYfBFRzAvP(Q6p)Br~HgYM3Z{MECz%|39)xZLq$ z+UlUIl}{C!SpL{CP_&s1brc+1jfR>|LCi_!sg+*C1$9ce_ArJWdRltg9I9Vzb~d>g z;z4(=P0Tm$v&5ZU_(SEMq0X+1KF67GW1q3CCqTFw@>VF;Gvs8Qip40$Q*de8^+}eQ zGAZ&?Jjn{=<1;wa;&v6TpfP-jiZmjyu&M9DJJyaek#Vq`Tlxi?J9hy4exE^h4drN$xfY* z&A%|Otf^^jP5C;$X8s8bUfbb>to8?tR)}A4=%~pCZi(k(VQjdI;Lh>R_zcJMpkczB zGlemFP{UOlhNT&eAHyFn9QQRQ4y+dCO$ay`>y(6W~5)dH>MM zglADL>8)@XJ)HN`{CQeqzKP;ar*1|#z{KP85tT@osoeKgRqq+XoqHGhFBHlva`M|qF=wmYENGj!zaaEA@mKE^!vAe?PlKKD`;p>zn$V%* zPMu}?pCfdZ(2IpO2>qT=b4G_Iw7AnpQz^NszQGp?KSTWI2+!vS*33towbiU?Shv=H zJ>|IXr%aqQZc*`F)gU1}3k&f)khu_mA@jO{h%z_-DUW z*n7>d-hV4eo!$TA6(xsS^lgjN2 z>GL>Z;jp!lc6VsX=Rn=)Wc*F}SNOO?=wXFh@#i}Hy3yjASfTAqGTi-s263}fe?!mH znIO(MQ*S3Xv_x?s%Y6Wey4kzvxR8>Ejf6EVGK-?hWKiGD5VEAejbIjzmuCf z8Ppj?7l=$RU2Qp|wfs=(&*&vwIQ0^&nGKhgdO^T1l3u#Oa>h(oGBT{5p?0Jd=Hvh8 zu&5H@w6h(26=LN$1*sd6KWCgy$4cG6h~qUEN^PNEq2|J=3a|JK&84MAnTH9Qi=@tB z9wuroBlQj&+$7EQPvr{VIa704slTS*S(=Mlkz6G4I(}i>nVR|o`c0z~>vR_5O(*AO zH`}Sb>3ji;0oF++8k#dpvw#!{p9$}0Oc?=f+YBCeHBj$$7V4T^L?;TKNWmBDEQC^T zVWLa6D8Fzj^WrRJ1-aQ+CG`wZZkUIxH{qu$a4=r{>Z#6Md;An`-(U?pRjH}WP&Hkc z#y6P8N=XA^*r8mtT&f=rYmdJ0zYo7<&NtFa8!YEvC1N^`-JE+QUV7H3t8Hz)OB=H+S46{o7D=CeEuaI#BM z)k5@n6(W1FzSwY*db1&IM(D%R^bEb99l*hBqM0z2F@M9|#cmGOben!uljJ%cfN zhS=$Pvj%LU_G-DxCUU7U(wlYSr;AuO!XeBudh-T)BtMkx@z^`DEUj>}Le8_9ZES{# z4yB^IWb!2*=WIvmYh;HqFQutq+IHLNpDd?#gohk?&`_J-+ub|xXmotciFf{HSKz}(G}z|%4p(Ra4^ z&dA)!bmutttGIJA^T=K#fr~T$#3B?sYOH0KWWGw@wGLrSo$SS#9GT8Kha=L>e$vVO z3%xIQIN03mXPnHJnes-5`WSBZ8%dc;(da6N-|BX=+mkY*^uOA9N%_B@geG8ZC#ijo zYrhIHeb*uEe7B%e(w7t z)Ka=L)Xw}Zt1H`9p8$uL!;F_BY;vZ$E*xe*qY_E8!nE+GW(~&*y~`>|>G$6_kb2Hp z^a)*yDCwo=qi;R1zunn1KGj6G{8#0G6)CvdRI73x8+nG<1 zFLMSeew&@SnzGAP%5HX>ok`3hr$R+{v+uGqdH3j43V)xS`3J_Y5`Mp(c^#9h7XE;p z*}?cLg@4S(!xbrUFsf=BV1>SeY73`?nE}r^#R`uK^8KuS^TB`MT;^oE(;rnwOeSoeyfMXTS^dc&8z#?YyGKXne;k)qH%SnHH z1r)s9hr=74hMqk`96q>D0+EhhuUG}Y z@@n;3HB_oA%4(Zx)&SuL8qP>l#XnfQJMXo7tT-#K?zM|2TbZy}zOWlH@ zz&NIBs$X47^dYOXYE9Kzphr~zOQ+)UCWX&Lq$sO&&P7va0|#sB!ubnkE}C9ig6K8% z-;Q6Yjfttnko1E?n*1CU4hAd$z0wKYt(x{i_%Y<*=} z@QrtF2;f<^b|w4+tz25xSm9Ma3bGz;gMNc-pr*8>nnq&GdoRG3A~zdqnqm-FgV*!* zo97VSw6OtKKc&?T^^GU>j(#c71uIq~DoS2izYg@s&ZkP&SjD4L6meBOR#@I^?dm0W zG%>C)SWLuGVnucFmOL9%HDg76{c60zP+ATML^N?N zaE(^XsRLAy(!vA8fzmEnoLM*Ju<{Ac2q1;dkps9)tWxBKpWg}u!9oDAoz8s*GKBZ=*DmY-f zR5sR>vtOHDhaQezh~UPdu?i4osu1k(to@BDSGqHxU{zJ`4u0pgK4rbwc`bnN`LfZwjm?3n0lQ8i^61$l+RXbtPi6~={$ ziV#*_Mg5vJd|c%rqbGMM%M#<^8FwcO)-&{@;hKnc^HiAxdl<_DWAFe`W(fY`0ppnx zybU-K9IhfI-G~zxKIx1xe&@m= z;A~xi{IML&AD1sCe{*3sabUIfr{fMV;9#u0@1R{jTY)QqyS-phjD0D>1k>F#NY?|? zrMpSD8g|MXhab(WUC5*VM*K`3814;(JChbM#%~??V7l7_>Ee{jOZQsX8He>vlNmvo zP3FPVkG~s)f++J;M%QEr9pyOHuhcSy&Srk{Bf9>K1KJ_5$@Zxh%wX-8L$ z06&0>TzB_b*t%Ig3=h$ET*)avH6eUre%P|UIZ|f;oh4wXLoh&imZTTbA449D<{{r( za^d~{xJ6zUPwv2)gYg|S2**4rb4FT7%ELeeAkVRP-1|w4L3KX~n#=H@N)9MbGRaR7 zI#=j&p{s;$68c4E9rf zFVT`$_l}_I><#oDalcn}84=y^hC2)$US!YM)cEy90U=fd^>SB@a z&chMV0YXO#ohbBtp$mmp3T+hHDs;EduM7R2&_{*7DD<$<5C$~UEfhLc=oLb@3GEd6 zTT(pielL6o4<+I)q?1C>sr0m$%gW7zo znMtqj!pXA4oVQ z+2-TN9;NP+U{mSa%Tw?TyAjp&DNe!R!-yfY&a=>)QMdY**u_dJ(8zgGfy)o_|bj)9#-j z=r|{LN}&scm$%t@D+0SxZo|CY^(WVD_x5^f1FQt8Kb||~$1z=OYyG%!Ay0bK#7DAh znz$MZZkq6PJjS+(8z#MVI^k`T+GDA0l)rWQUswDdj8b#hV!Bd>Ym#7}($mb5@3p>x zxe))=`;3gwaO8*KM_#5wYYyx|LqD3tJ=D>{#SqrSVLXQ8zQq{hHw6yHPCw2{96`ny zzjNV$>Dn_qO}q&l$0O^&lw&^ZCJwCDVlbD}BF6a51s{}m1sGE=q()vj>?RIHJO^eZ zEn-YPvur_mo4{}qW&SwP8F`Jc2jzVPrmHgHKm6L;B*O&!*q-oeFkaPQ1{o$0 zE^$w`3E>{>A{^63mOQf;Yl8?Jv4!)nfm!b?D@1taAR5)|H|R(?U74W!@%LoG6qugr z4O;Sv?8$;{l9;Q{;yqUO9U9IReD4&?BhRqjyyqd_Db-ISJXb1K@$sw1M-~Ecyd8Jn zgoM<-2{f1CL1RMI`<&o8rs+Oi=p3O7gsu>(p8W{N^9P2%M(B1@bcUTmR|(~qb5OA? zBmGr=@yAe>lkU7=2rToCyUrUwvG9c7Tbzb|2@|)@Yk+=iE%aY&R{#$yR^2{z9`t#O z+72gKB^_AOj|x4!=N+RHYZq-gM0e=bo<*J4vk~WP#Qh}k8bnW5 z(?m#(=$EAU+=AOb!XJmX&rmR3Ooy;j$M8wc4icka>U<18XqAqKBeY5_{!AvjX&-As!WW+s>q58Yh77Y6T_eI2xZ0Fa3~k1oVYyz=M`hvTETBK89%zuN$@)hyzZ3i zP+`sB0Qn4sOWa$i8=Lx z7=6JPEDgiNiK2tPul4tAN(kfgX2~FXb+H9Hwbwb>`=YM)I>DZAR$aVT>c*iUCI<)F zMX+b!hXLoUaTE7CJWmZ8!zS)^juwvV8WV?d815?pfyQr2LbyBN!I{7qV@=%aRPtaR zo-7B~Ff4Bvzr?*x29~40fV(k9UgBP-5(|FDr#w<4uN-z02Ud&cr88&|W9m6^uTu+o zQG}tqW9@Z_56!X}W7108>ue9?k8`id-&WX79LiV=k0>o-j9)AGU_ZD4^0?+>{$Taw zCGK^uhrBi;1mS4q?@pm0%8WxJOfn3piITY2`8>i6>LOg?US}J^oycBiKPvYi660Qn z>vq-)%f+$=cOp%ww7qaM#^lfNh6dgL9(x`3HQ(4u4~7d1ZSW2nEZYo=T1|!hnE>xu zVfiffM(i6j%mepEQ8Hp!&%+G^8pV&#b7P{g6dMMVd_@A^1fH_FE};A^0e{Nh9^j*} z-RY>!0D&J`;4Q@MS_*32hYm1)*#&#@j*aK!;f9P2#>^=tJWE6QNIt z`v*e#e4w1D&`Cmzg{~56mC{4q2l?llcK;%=(;wz$W;$Cy}1 z^k|yn>*ei4yTZinZ+lu}e@*P@9AZ30cPo78uyuWB7`RPo(H(JE(h*=z89eE9iASuZ zaAap^7#PtChYFa}#BpM{F87(I^n8e0jiZC3==sF&vuE}(7*1L7S3#$k??_r&}m>?ts&HdiE9m^$B}q{;8yd#*bQn3xub_@iK#p2C0tl((iko+ zm1hcuQyN05Gnz?%vBmfZcszW4QdFv-_%0FsihR@X$W1+>WI@2ay<>9M_DW1 z*75qMp2c{chENaY!P5}Bg6-yM2z`%!o`%q0881#l$c@twa^p0FTu(!&7vp&vLe#c# z<1~a^PebSs(|bzib581J#`83Ua+otuLx`&7?(+|;>V$^S2t+&q4WTy?>>o=*=tkrS z+KJC*zQUsW8bYgCOGXDMl({^}hBJS}nlZj z($^4brLV6cw4A=ahEOBZ^)-Z^VM4x!(ErE4zJ?I*v|V3A=t{=*HH22v*Vho@$aDi5 zLah`X&=9(Ug$QT}O`)`ahR_J|M`{R7VQ~T)LRAox=0ZbAO|8C$&?qMFYX}Wtv}0%p zaiNy(LPO|72J|(AE~gk@Lx}2n?lClk)OVRgL&$}O&=iLEHH5g&a(xY)IxzG^eYhJFeA@l~*|D+m1 z=&A!aR?$pKLx?JXko}ox2)P9Nb4K&)LuvSi2C{1>yZ?ehJ2h{^?X>Aw1KAfL1NI1e z2%Kpq;b$+;s$K)|#i-K$+Z8^25HS5MdnCiy#e?y2aXa@T2(V7$rLIjSV|$8{z}Q?# zK>{^o?6atUWEX33XK8UE5jQm;ZkiGoU4SgQgIyKLwk`nY=r31%T5rr$*3^HB$MGk3 z<{Q*^vdj+0oasZWLTe-$l_k`zr?Lb)H9+*YEIgGZw6S*qFx+;#16XjD+dXfyn|?c( z&GH)15qAJlMiawk!yUl1^o%bhwc0JtHEwfgPI`auUZ4IsDoh%$3PL)JrT3{y#@Epi zT>PXw7Q0rdFbbZ!>*Ig3>ZXj+fTqLA)hrbu>ht|7g*Hb}Wy2`xs#wZk*MwApS#DO+Iw1CN>d|2Q7K-pgBl`K)bT|n9>I%}hSs0))Oob(SezCP zG_bU)jp*oj2l{b(OrrTAS}j*urcnfmGCtZmOOi;05yRE5*dXCWp3TOuUN@+)`yeTy;uLAVVj?!uK?`;A~ zMx~8PpT|>>!||0?ViMgRjD&z{l&@-(s51mLEu#n}T2ESw$kVtIT|c8hsFj*nMx{+> zblU>4Zk*Ql)sd{l@(K=bDuF6jKs?6%@ zt}OVNz;Z>I)9@D$8PD9)U!O1XtPu+b?~Ww#u570lLt7Y@Ch@N9XyIZAYvM2-!+km6 zV60OT!g1xyr=&3^T;g5XL6l=83dwTtJe1|-S}XCc>=9fp%r-*cZ>)1+4%qq*pvSK0VnHA_S7kHd6 zFW{uCCLJ<=kGnG7efdUz-=)xV(05~gy#{l~^08<6=F|9|3+s@EY4YxhFDn_tdd)Tr zDEWN+7&Zq#hCd6`82X!~E?&pUQQvPd51h-ZjV*5ZC5~7OmwF+%GLAx27WxG1#=)J$ z9P(&%br*t@agGTmMdyikHtg4=Xm+IoqVA5+(Q}1Ya=?F1_$fj;FHmP>xzJTYHwpcs z&|8GwBlHJC9})U*LSGl!DfB;ts=Fbib4s^M*Y_#iE?p0&bh~st#OZcnocsX~KaR_8 znAk}(NIAZR&KCDY!Y>tiqqyf`>h$IXOuA}bKzzo2h&nRP9KLhd8HOn_Y*&fo%VC(uI$kaXkPwwzX zk-q2I;97c^?b z!eKOi3~$!RM++B27!wCp^`7A6w1_c&Q@|TLy?%_JexI3~@0YlbBf8gO__NHMb4@ws z2jakLO@wg1XJd>>i_-?Cc+U{ki{+v`QX{V%b`u9y^=*hKEn-YPC+_1SkjG~L_6Cf`tc_T4-Z-qTrUVe1j*(uV|myfq%3Pt z-X=8a5V#p*F4kNlmG?i@nu{IbsB5m=zj4h4;i%8!uerExV%WcR?WGH2hTbQ? zMLD(R!d~K}fyqv-xlXOQIEik>FZg6WwdOjt=HgR;=0v`IZqCY&x#rSSy_|i^1|3CQ|msb2x z&8Uy8%tD--MK$2{^67lC{_=^T^pRD<@<~96c)j|mof z#3TxyA2GB3`B5>rA^3?v*t%ZJn2MnP-?)a6jGGic`5NZ$e`NH}fCt2LAa-Hym1qj_ z3%05`$6#+@IJ0-227A!ZkA{0UJ`0Z*ZVbYhIIvojH=PzS#*gPa#!jzm@ni2a#`v8D zhu}HJttt9!iRwn!dV1#=-jEdmuYq=T{xl2jL3wXO9?L~}uzK=%zGLzLt3_R`C@o@4 z{VfF_l=nV_u%0Q8?hHryJl_e*YXH;LxyWjS3(EU*Ab+@i@Z_~7$oo=4{!Cg|C&&vS zGbVr7ihJ^|7kM#In(shBv$E+wl*iv~LbWS!evWbu^bG_+UuiJmh^60Ec@xippdw=3 zZz6%3a*)EyGn?44;mBudPyATVEEns}^if!?O{l-Vw1_eC3~y-A{rKlV!JII?V2b2T z@6ghZ#QjN8x~wK`zmz6?um?~cTi!RWkIbqZT~z}?f7XO=T)gqXGd~*o@Z85d0`xhL znVnDYOa~7bb*2M40`jPfn0TgBLw8h@I>SM6%sm>Lt;=r)HkQ|x)vf*n>xqfuqRJd! zH?c@JPuzN<8`cw1s~E$ytq;}|?q{~9U!*!GTOKEpMIuJHIZ z#c;dz_-hIlsjD@G63DW4iA`cEg8qNon!;qfM)#SMxQjZqrZ}08XLq%x2)3%3^@gB@ zIiH$Ykv%$S%v{U4gU`m}g-cvhP~M5GDRv-SF@DCFbQ9MUZFojd=JD1PZz9ZztSR1w zJeKQtYl;$ND2i~#nEFdxQ}El_oVSm+rf7n^uAZ@pYYJx0U_9^EEF5>jcn6y}7_sauRAeOjLm%{+ z0N#P6@eV@7F941~n!zvtenAL021mm?P~lD3Q+NmZtEhr%@QJ@`dbA^<&kpZkH>Mi< zH<&{VST?^$DR>7wV*=j6chO^jcW@s(4Bo+A^zrcysDmqb2j8X7QSc6QtWY)t1@I22 z8}8#BJVb7236fNJ2TRCmyaRq$&BHs0f)TufpQ^m5FQQ%zEAb9KMy#XY9o$AQ5AT2| zi-t=}9U0(G9PfZ2Sj0QH2YL4K4nANy9^Sz(=;z@b@Dm|E-oYGX40(75 zwG`yx9WXDS4DVnzq!I7ndB*hc4ip?iBHjT%MHs+4s08~7@D2trqaNNt39I8Mcn4Q9 zD<0m#hvYoG1Aa=;$2(|YyLosA3Xj3VJ9vQUodn*&&zPQvcd(uLJPO{yJIsrRcW@rl zdwK|BJK4plZ;|ux4wPoOk9RN;5l;Z`fOrc3Sa=6_BS#AF;Fm1Ak9V+&wPX+tLYYg0 zY&dg0>%#b^Wo~3#AMaok>)XdWID>J0yaR=3;Nu;<&j$DL4t7(Nk9WW?hXn8r#xh+W z?_diP^6?IS&%i$3!A}_2$2;H)VgbAZev2f4cfgSu#5*{Lq62sb8(D||-oc;gAHX~4 zAb%v@0l!cZ#5*X57=?GBCIBDr;B98k$2(AnEXTk*7>d*s-oYOz-^V*pXdyn{fkLx6 z2Hrsog*IPACEmdWEToTjkVMwUJK!5)0lWi#g*SkAu$3k7@eY1WUmx$_HTwE^2er($ zk9UA$q#)kGEo6PXgL;Yz;2q3jJ_2|L)berzcn6D_Rsiqdixd~YJGg-U0lb6r=^wy5 zSV;a@cn8%8@Ja9vUSfuQyn{VV|77qEI93hb0U-)MGrR+SsNKUm_z6l2yn{19f_Mj0 z5yl>F4}micw{1tkI~c(*f_E@7fOlY{IChbez}UP!Gk6DQ2JsHgjK@2e91u4};~n&1 zje2+o=Yw-JrrM|TTDa1OKibjNNx%=E+#y&z-a(_<;ixk_Wbbc;I~W#(JLuuV9dKL* z?-!%sjbW3W4A#L2#6#DC@vsh5Sp@1p*-rxL;1otd;Jat|!fBcM{Hp#^=RW~P!O0C7 zHE^NJAY-Kqpabp4FL?T&=lwr~cc8ORUC4hnmf55LF zb@iSmml4KbjigMXZcKB3eJ{!LODq=Dtu4>RhY62`i84Ed8p5zNqw!<-T?o-&05Pe1qir6hgVy$MTc_=`o$~*_z4tn2-^)#Md8lZ!f4NzE?Z?{Bv(MUlt+O`f0ej7Z z;uGh6za0a|*@J1o@4R0yu-m}j7}$SDt?r-xJ8E@5yf5yk#e6j>E~h#%eG)S>qrZyx?>I|dUnC~%#sqJ0If_rDzjJrUFyQzknG z6AtT}Iz^nl`VQ-iNh9#y<-GPkXFYg*^!{(^`oF1joY`Ch)V$UIz5~#2$3W}3+k2}x z5bhnhx9V-L*8fdi=Dk|K9Ru#|xSI`J2)io@;k@Er>$o6V?RmM`QVfi5PcCiYM|$qr zjxIX}we_p4D`@+=pXW%~j>1m;Xtra(k+pcP;6pr)eZ~**-v2z8o(MhiT!&}JKoMuJ zzQa71Gy;ZxIj{S9uEsj2Bsf2BURS-gsTpS zo~xhd>bD)G7A^Jew@;hvDH+IwS zL3Q;L6IKiIIMo2hp%#_PqjP=s#ZY&020kp2%7=Me#a*g&xu>htSQ4&8waBm0y(%U*_{O_O&?4cX&LVw6wlem)y_O9X;o0_Gk+~ zpRQ+G1{oOFV9qT*;!GF8FI~@c1;T8{v~=lurah%g=joU(8=me3q)Xx_PRczK0k$l! z97r?5*beHUNjnz9FL~f~T7W!3k8u*mMaibO8wmS>^azDsHT;qXUZ)ffBiklU`k5=F zP45Rl;s_(X-tH`t>HeycP7~LJUyo9iV3ldNL27;^VjX^Cpk9 z@#~=XxiosDd7nuqQFCwz7U)9;)eg3cV`0Or|ykn&1>h`bwmrcj5noW4to_18|{rNucUILB(NpnmV^ zfOkUgw6AlvA{5Q~@n`f*Hd zo}OkPzn1h6UTN@J1F4hE@LLVsZo*l2+6^0Fa8)1RTu(_?)eHXws(K?FO%P%_>d&Dg#q^Fl>~B z2NPaPV8C;=Hdocuz3V&|_Jl_>&xPJ}^w}IYww8f-at8;cmv#;*y)W)G)Dxjcp6l@R z(iCxYMdougo98;b^E7FMJaYA|y5xSItDooM?31@dKhG7MSM_$D)~}bwS);$Xw$H!% zc`o*fJWce!XFQiI`NDJQ1z%nOsMpcQbFFJ`eh>80Fj_}5&qXnT=D9eT+`kLGG(8b| zw?seB)z5QrKBUVq`|puvuV_bG zIep16B`x85A)|y?M3#Etx%BHp7Jk(0=;OJrtZG=hu2BxU`uzTKS|X9{9Yp0}H6kf` zY4Ng&uRG<*j=$~S|0^e+>~NgLT{#Y5d9n-ph&&gNc)aY}`*=SQ)8#nj9sj$3f8HPW z?|6P9 zjpVpX3CFla0CZ*ext9z8b5le~vI{Ztvw)uS2>%}$yRAyZ){}Vd5bU;MYMXhYcrsX* ztgTyFvRe07j_S2gky|q9r*&QgT69{!%(kCupJbJxopT3AZgsEs9O@0}PX8o)7vd)+ z!JC;uD^oH0-{CQ4ev{U%r4`J&+IT&_IrJ-V@O)ZB)7n*vhEGsg&bSg}E7}|t>nC~l z?_=U;m>-0D9y8fKv?qmLb{sXgsGlP)9|+f9byf|?blhB}>!h_I%y#PGM?ktxT2JZH z`8lS$*di$OxMYZ*(LchEPk}gzOV>#og?6k*BDRBa3u(t<_$3d#4(%T9rN=mlS+koTR;jY2K;r(JKrW2b?+YD_yQ}HZAA9QOW)w0dkZn}9a!ho9 zl4AmnM?UI?F+bsS1LqjHz`#WYe%!!XLd?_E2JSYH&$5S!z&7@-TZTsQ9`2?|immID z|C^Ehoz}O&W+Pk*|F<7^P{tDXe$I*C`8g-{0zO4Z;#Lgz4)S_Td`SG=5GM*I4vXQ3 z9D>}Q?m&v9a(n|zHIu0F<^{-E`7<#BQ5P36kz9Tes7~VM;hT3q?{#Bsj` zL^wqDIygiwe&7(dF~)F+8yJ%tGJtOdHxl}r4#a=hxeR?p1?GfX00-3JU+6AI!QPk1`cVZ1(kHy3Lec|*Qe_zVcobdte^-&hLVcIp{IKsxW- zQ#!u?BoFB@-Bx;xlepQy#m}hU;>V{=oWz}p0J|Lv(T-gRV>^1|oqr4hV}uCOiZ`8i z4&%Kb{X!`HQf=jd*ZGs@IB|N6lYYJwxLv<|(2F6A_2cV9=(WNxb$}Ol)MdvzkFx5= zbxP>nm`0Dcc*PMWPRiPd0K0x;t@`!GJ6{NTCkYYIpqIb<33Ab^tPVfaC! zS)zF1-3fwN>A-5G@VYM`T{gV)Uolv56h_{e{mp(MWLr_L??zz|PWG{&epZn3O1+7^ z7rZlhTfSJdvunfla3%%rNZ1J$xCy+5XXM>UwN8$R+hl^8tXs&{wT+{r3utbacAvsJesi__KIfW5|oDXjHCVXY6~zMb!(5vVAf=Cvm% zzvEmE=z=TvdF|Cqk`uZJ0K7IA5aG4=GA1`r%vJG`$KfYWJ#aq$fv0{IAatyv<2t_p;yK^p z|D%(q=Cdl77^B@bSDc9ze$sJ6CoKqFToO8|hK1{w-8XhpiKIcu((aCG8%BC>M{rvEeeu-3 z2@@xA>HP4EXva1rVmo@{hra~^T|x{{=%w?+oA3^lej${8skZWv%sp7!$I)Y)^m95t zd<*EskcRbx*Qb}x5AOjz?rp?L{nF2p@3HE~HB0K3&JTBiUR;O)3cZc+^W7ot0jqw! z@xvhuG>>7#(aYa`21@uSRA`1ER@}qzgG94L(dI`s{BR4>Wy25OgTdmNQeS7uJd2M@ zB>>rmOwJB}FZf}*PVWT&D`&}ED>9uW`>!?s>{z{M5Uk-? z^_+M)=_0@4djVG(ywSim12-6`_+QNZ+lT|+XTra2;15kWpT{iEm$gmmZ28@H;Cwm$ zfjD1QI})-aV0O972+o)J5DL%xX~yJ+47eCr!1DqLcwYN_c^@NkLp*Sn^W}Q@E%rA~ zRGlwt_Sc**YxcMI^JVhFe^2MjVuBrmVsp}coG<^d*`sg`d2D7ef74X(6 zJZlPam~6cCdiLkl2lCJ<2NNHiGv|!>II#cY<5NpYOG@M8pgvJo)mk?`K5h=q(w8)? zYpJPgxhg(u@pD=??I|ICk>y)%!)!0^5 z4cOMA;cD@HFtjtsgYn(Z_ZnR(esOw?13RKzIzQcizO4HtouBSMU)K8xzWq3_61tn5 z{_|zi5de^%2Z+M4Q?T5j+)GJ*G;prcFFD%Imu1Z#Yi^n}Z<~~5{O|C5S#ug$^4n9V zOq-$sLTEByo)-}njRGgctVnU4*>LH7cvD#LlxnPM^&+bxw$AKP-47ni#6rUc+*?70VKJRiU$7Ti03>RQ=#j!~4&rkKDPmJwbZnqi04PXE!Dg&u1{*{qxO}3@)6( zo4{?yv~=m`(mkakA0~O2kLkE(iIce52oOKxYJl*G5GQfz=hA;gJNUj}JGhr%dn5QQ z#!vFV>-+)81S>$i>3sAiFf`0hdW1rcPp0I7*BOfr8b^AhbHzHu51Oh%_)la=D5XN?=^U*hh-qTh-(&O(w10{;* z%`*hC;?mEhZ$Y|j&ZQqlx@^v+Uqq$bQ6Zj7Lrp;S3;UgIMb&+`1A}mrkEM>O6`V5G z@5Q+^&y7d;6>`r0lz3{vf=hmXP!L=ll-UinRaqQ<3K>&$QPs7X7@cC&tpI33r z&l5>;#A^isB+hebam23>1d#YOX}JGxvh_^#dloO;^;$g}KJ}3s6Dw_JdgMIb>>3c_ zC)Sok5u(k?!=sH*(?d&~%&x5|z@tC$ zG6Uxt$T>0!gI7ri^@r7jSbUlZ@o=msJjQW05FV?ag;A)~7UH-l@-D)Wn(rQk!xo6oMs(W5l;YA)c6oQ`R{U zI_%V=ah>Fy)So$|y+r2iLtoz{vRmi|;ioL!i^e%O7T7O*4Iu5CM1)WxU;yU!|f^k`Td4==1S@y=+bG)Xl}a&!K-1zCq-C!BVBx zBTj%){Ok##mk!4%LO9c@=MRwUxa}B6M;+4$eto6m`$O`;tLR-!k8u)5ep>vDx*tEz zY;h8I1_H=SiEBe8)&s#S#=+~r=R-dRzvO{eeJJuAJ;q5K*L|Dbo1n*bkzOu->=)wX zrzH=(&YM8u^cW}ocM))#-rLB`wV(7DF7)_5vFYXGVa~*NuQB-mlnaB1)DN#4pWa6J zZF;AIUR>qS|0It5h+V&YAW}ar(o(-W;Afr$est%8-ecOU{xgohtp=(<{2PD?F+&h5 z?o03^s#&7=4PrKY_X?!jDVZ#KEL*RCI&uXE3xS-|79_>OT zl2pYS8A`JW;U=0pokVgf&xYSc{)_B6-I3wH&T@-1ImPyC{_D)s5cIyW38Cjm&-~Y! zunF;4HZ~#HvAx=a(0ej=7j`XXsS1_>85hR;JkKHXV!EuuPu&ZhT}RX@J#rGDw>r4^tT zN4z*GYa;?|dfTk};dsKQ_j&l4hvP?g4d`X!+wMc0&}Ody@ z;Eu$1Y{Iv3t;nRuTND(h<}dj=S=pg?AQ(OV8KFKJ)Xxodd?$d$`^+YUzF#v-9fB#P z*`KLR&d)5X zj#VrTwj!%_wpKF9+%|cjO$5F-#IXZ<RO!=@{qO4q-#}6)!J1I(kN70osycS#>P6Fik3hV_E0PqWajPUVYR19 zI&ZfZ?>O=(1>h!yAPofKVk)p zlRBmIcD&P4`h`$ql~>9XPNnvpIW-tHdoc3FL+g+VRyTOag` z6{NhJ^fW zBK)ce-(}!S1|EVvFMYS^d&qCc-tYYOYIMHKIW7D)M-}`w2OHXqe`JiYpPP~2{u`5P z6-O^8^gCcsakT8`{(=N@LpHzNr~TZ1e!K3+O`@Y`pp z{q}P;e|)3WN9?xgU;*Rw7$^O=5xCud{q}REhf<;R7en8Rv*@Fe-{z{F!fzkAjQ<_- z+xlMH_mJPl4Oj2)`zqcmetV8vWS;ssV5&Va#cyY0PwemQd|OZV15|ALIN#oPxc09)bua+=E zJG`IYMu*^(MBYEXJ+TbS_}?MFt?x9>mf!wh=>jk1qXKb)e=>a={0H-${S74W{?`ST zd#$l-*oVIFfG)5$r-5F}c+MM$#L{$u5A+IR4(7+5!Sm3?&%U&rBS`sV(F{GFFQ zDSFa(=+_12Bjq^h`VRfNz?t?K?I5yM0UbLhFCWrEDW5$~tGo%Q~xIBMIMebq#cu zU^|xV0@HAJ4(=a=t)y%5Z5QFYX)}qR-_ae5pF1D_M9biI_`%ElD*)659%78qGJsbU z3ZPe@jwWF-&{E1QSY!$pHGDa&)tC9|Y4$%GMUo zAM7(dc}O`gWTq6s&vX#2DpzTs0LIasj33wLzS5}&mKltz1iV-fK%qyOxA+;&Z-O|x z#Yx;52(ZtZ?m{J~e@5Fxz1dDW4FrB9!~lgJb=+)vF(7Og>E+_bej$D-ev$`XCjx!8 zI6cNm|6K&!rbinAF@%vG!-ZZG{5HLDY4jv+jmZa~+&HU#QG^IRp20~TCY;v;J?iM$ z^-Eaw<60~9(rqWL2EA{SuyHJlzjWJ4Ct*U&kVGa8(J$eLsAl@eiX`ss4mo@OdoI*?6n}+!9{BBiiJSSd z)=;X|`tVpM6p2R9#L(u*RjmVxKr#12&U~yM5yTJH@dJZ*Hv4`-(yjN3) zJw~@2KNaB3qwk~qcZ#xM37`H(_mU8plJP3K+zFE-2Du%%vWnm&7`$TMRMZglF6mbEp-MxKx5KpEzo+A+Nuu9QU61y zxaKQ*_Fi^2Dz8FHjz@i~|6pWhwWRZr);T;5$!~>UW`$}@#Ub!&>R{~Mw>$YkJ_EA{ zT2}buG3fSc{Z+hg@Ym<}@O%Jmr=D5_oQfaw%NwPqbP1%9Jn$+Tl~>SXoW#urE`COR z2S3hiaS}&<%x=dHRD!Ds+syX?+sk^T+d7&K0=p3|PUxkdfzf6o^OGK-(5r@D^1!RU zhmX@^ob)sKF}r@lf$*J1dOXPxdTOx(E{?X7DBl(*Wsx7V>5aAO$2CIe-3Y(rA&u#v z7pKQKiQ5R=rZ>T=UvJ;TUkG})2{Ay@bfV*x!YNO`3kB7O5&b_py`W`m}B zAYa9q8n{fhj>s#r{@U5KA$`uIz#R$ONAk@N=V=e${P?TB$VAQiM%I2+Uk{9OR(u&4 zOZndCWR%J8bfyquP){?k(uA)v@DUTvI(q1W5e8S~;h#YE8|hruJY`*t7_9_D)z_KV z=~Jd@wkwPKkN9m6oX>aVbEd(zIwmEUq63e75O%0q_FTHnErQ&I*tZDwc86de#{=3b zOa0oFO=6*?p?+=M;VbE&hO98VhvPw4s)7GsW^)lj=$EIbr*xbjT#NfkH^I;YP>%GZ zECz`G49m~(Y5Zq7Np}vy#LsA^i7@WMxI*-_27VTmu-^ef4_Q*29p3jEXxD`8l+n^84}ML=f^Ipk zk>-J}aq=#MZK2xI8UC}~*ONVc%Ji%){lBS~LjTF;jw)6*@pZQ>*})sgwmUC&>`1)W z5sGYRkClCUA9q;!*l{_#@9cJB$&P$jr$xFqSos5YUBPZDWZxjhU6$EJ<(Tjs?yd$L zi(OhY%3H&*;~L~&AchA*He&;eun(Jr=)3|eZ8S(b19KsNh!aJUK}^DeG{M106wx=5 zG0_kvVyQ8cEJr6BTIie%USn7xGM}8!1O+*2?>Ag;0Sir8s4>YLV-R3H`+L(??-I$I}y-w`}v-w0N%;s|y=D41edh;odb>f!vEa)hzS)j!ij9e zzt7+|kRHr=ohw{|ByJ>+2S$-lag+~qXfzA-ik&FUy@bXrRan>weFBAsif?CN9x0zR z4&MB5h$0KG;6L#{ya-9cxr5F^Ew6`ngf~9=D7lED~+T zsvuZ?^ta4rQpN^XtarQ#XDb@Rogv|NloeS9^grNHEy4_H2Tr3$W4+1Izp)$FmNnX)G4~15-}d*zjnj3v7nQMnnfP<>?w575yUP z&d^xgiH%3btMQAt-kj)q#?56A`?P}j&L$Qr-rz>R&&ZFm89=@0eAZ@xMmKp*k-ZI^@7chu~X|LM5P*))wW#@Pwzd^-CqO6N|5j!YUd=sw843lo; z^Z0iceyRgcbcVV^!VDVpef+Cp4|{ddZ^G}bWC+W6jAg7cWq=GPRA?`YYMKh~pkomK z1b!>LFAc6}cD#Qv83$LiINn1h-{6WX9q-E;iA29nk`FVZSDYVxjI_R@vB85Ys#iMR zSHGwVk40yp1n+A)--zgg4Bf%L@rp-9XA;{vS5b*a?`7z>niMuRdOYF3-KMZ{(Z`tb z(FYYaVQ|H|YRCJwE+GL7b^ea7^9*39^uO!!W=6ls98Z2lyyB(NJBU61glfZbC%l@ToMF?E-(`jZ zwyuC^0a5h#N8k@Xi59q#A>q+LhSjDfCMt<9gMT=43<)p5|6%L__c>(Nbd}H0UB`I# zNl-3weGUN;w$WcVFe86x<5-Z{mb(z#GKAK zzmikfMrxn(&OrZ!iraGvCoujR??n~=W)6mcy`96az`c$gz_KG8g3mE%xPpwwPV++-zcaiDsd4*T7KQHz|s(Z@|zstlcylYiWmKX9+!n?%d ztII21UHA%Haj8L@3l}kOg+bRBPG+h~FHd#t2Dfk%@hWem!f$d5X_e5cRwajulWyU7 zQmawfL&Xocg~u|!*5F@t3wJPooxyjwh4-?!l?H#@EqsIdR~dYdiV_zVkd@`$rrXR|)dUbRBkI}v)=dy4#MC&DSo zAu#v|K1Xk1svgH@6~pvnbmDPQR`PO>Wjc`u@ZYb=2xlap9=CvxHzo}uhlKeAjpXcg zWsUg{75+H>M_$7}&Mlc2jUXNghc7~&a2SOWJANWKAD0ZcJKW_PgD#Grd&#I4@T4rw zEg#*+1VdHYr6`uO?f4bU`8BlA9j$UQ7x^al5}jO=E_Y6t!UmogQlwmGGW*FnhX38a zfU5*=3P0-LxQBMq48>9y8#u&cYGa{bQtMT%JyED%+uByu&@idGe(j`^5+2h}T2)gs zsj8)M;@YOQbrb8Vs_Q3BEh#N24eTArrb8)9wA8_lX~}A|+Nt0zs;$}&^I~hX&sU8_ zFJpBYR#-*4p~o`nC0K@Ge`}w04zK(TIq(73i-v z6~EG{XgIC3s-k)wd=*#LtOj1avbFvbh*j3r+UgtYOzag^4eL}UCJt<$RkYN#u4`xm zms-)Za%F2B3Tv%jwYI83XM?|DB|dL;DynL0or)`4>Q%`#P3s^9s#v8+HdeQ`Kp(57 zRVvm}I>V`0TerRqwSukLwx%@|4OOj3U)NaIvZ`)v%~eiC%@u8E3X-Y1FFb$FxfP4g zp0jN6lKB^$U9lA5^-a~SXw*6L7oN>_s}iJuRdsE(=z@y6mKG#wYpiIj2PvInZ6hXA z6DqtC^P-`i#jaf2>a1*Ns)Cn^udJ=QD(xm;B&ljyh4QLeYl1$597**f`lbT(bsQ(Pm5tQ+R6~8pbxP zZUV37E9oe~4MwsGLxetA+k`}z4b@gF{j?O{r68DRdK{b9R(;Jot*fozNr7sQ8aGZ5%&1z9GFo(1 zT@?m1cyvrAxe%FYbizgsA7_O1DAcTLX~7~;QG?M~)zpIc`qqk?^;Nn9(wbY-P*=4U zjIVjl*0YnAO+=VDiA$FvmRt2h7N6dY@Jk-j_$?yi^cW{`8-d&HT@HGby0CtO@ss*}9)9NG z_|Xj=gu6r;X&mGDyU#!q?7TcQwO^Qq?qU2eSDF=qf5UKAn~fAPfppnO5my}JIAtiT zFDc?Zm@hm56esl)T%aA^_X)Z@1v>Y^pO{C|^sihs7Nt@d)zdzLe&kFF+>wwXV%U_6 z<3FW{m=?a{{5ToEIDT9+7(WS69OLEnUQ**|A*20nuBdG?FDl_~?e~y+&Umv5=h|ox zW4k>p=X{>~)DpwI6lvsmrU$O_19F^+b8R8yvrc%4fvXMNWZ=yPsvSMjJ!J528Th1u zKQi!T1G^3UwSjo^sB+Y<3UtOAoLfDXUu|HsfuAz)76b1#@F4?t8i;MO%Kw6a-3I=~ zz`q(8^>n(C2A*l)M-5zNV2y#78+gBgUor6C4EzrRUo|iv)1B?$VKU)q2F^8*7V{Zi zY2aD|uQqUpflnIva|7Qr@XrQDu-=f)u?DJxL*RQ2{+|Z!Gw_cF;`(Kkuh77;22M3_ zwtX|6tw^b8yF`Lb&TQMB>Mu&EVK`%Pn$7As~=S6U32!r zbMz4kh{3tIdjr++-4Dp7;x((+_Hp=}Ww;Y=pV<8~XG7*$qr|CvdeK6b)A)P zJKf#4bfSKFp~TPsJI7hkcOKPs4{#mbC6{*h5e_cN|iA`{~W`b^t)ixf%;4q?|>F@x_~* zSfThl{Lke&-h+74l;GXWA_MW}H<)>}Dhbjo^3Z{JGyHv&rp22|I2t;MJV1Upd>ofu zCEoPDgd|41`2xHYZ=MU7Cf?Na!_k)+b*jc9QHo;(%a2k}Dp>a7%_iiccym3P$l}f2RO7*5`EI&GlbvR99vFHVio363p(TCCJ-VBY6hz?=O(=|3K`ZnXv z(3lZ#{v5?9@#d3^^TnGVXTCtZsbsBzc=L-au@CX)bkI}cP1Uo$c=H?*zED>o9DR_5 z`r=L2<>17dwHR88Hw#%IU%aVge5*_uIC%aHQiUgyc+(m1D)A=eVVT97CZ85>Y9tbU zh}HJRo7AQ3NxaDtyszneBcg*?F<-p-kId(bHy>t7U%aWLTfTVnc;-6@@un_sX7pcK zPhY(GSC-I&c$2$P?`fTHVe}fNd`4sC(GyrtU%a`GgXD`hy){f~#GA}ui8qO&zbSt3 z#-IhM;>{qj5^tKs*@!ny(m=fFlR{}(i#P8;wUl`C0XA>CMVn1kEp;#l&ZtyfH0T48)taF!3xCn<%`Fi39N_cmq6QCU!>Qx0yH)Z=TG=fp~Kr z6E84{=N8s6HV|)K$T11Tn{PAm`6h9BVGbz=;>|854#b;$GHvnZ5H{EnZ~haBS>jD4 z3ADtUCo%p&@n#XpTH?)rLoy}aR0}{L-lSq9#GAVJ!-eyR_9EWQMZ=YNlX6Z=ys35* zfq3&lR;p+5ruREG(h_f~&A^mji{1mBK;>~faw?l_m~0CWrSgCz4_wB0>P%Q8p3sSqyi?xN8=mUhQ8 zxx4rTeh}%NqLA{@^^U7J1$V^>$D{2ASu(nmQeea=Vc&0lct!t*|Me)*!sOO`IX;KJn}zo@FZrnYY7s@3(Et!Zdn+thq{OKaP@ zE3RCB)h9SCIO0tC(u(^%aUA)%gTuFCw0s#X!?oC+Tw81vIkTo zkHB;?Gkt+`GKs55l`96~*TWXOENP3u7kV3m9qqMi3x5DQsS-Fj3$ zqc~P1R21X&Dv3QTNt&6+N}AiJ9M_hL9kfhWWNf-!M~!H=MalS%kbGCG17soPJ(AL0 zEeAg0B3<=>!>X&1>-HkbJyaR2=wI|8gYB2QLX=;kR3A#JTm5^6tE#J8>aMUaBG%uy z1Q!$Q?_64%bk-nzWlL2fK7a`Vn%7mYMbLY6tFir(kXJ{#{55=>eiu<*#dj3b;YDSnc5URN8SH{E~+>eu&68 zJ;q7g&A@GXp9MXhPqThQ@DqApgr9jh&UC*5y{Dv+z!}Himkk6^?(2}N+WjKsZbvM5 z-e!}``6be2Bc~1HT>DAM1Ss?fWy}~|3HdAKUJ%!+^LbX4?P6OMuMJ4IcZB0i)?W2r z=vhGrgufedTDyG8VL3{c`l%m=K?BuE})X<0xEefppxeT zDtRuTlIH>{c`l%m=K?BuE})X<0xEefpgO_${!~@EHQ1V#^@?1bA z&jnQSTtFqy1yu4}Kqb!wJQfc<=_q+FppxeTDtRuTlIH^c(WF!IT;NKc3#jC|fJ&YV zsN}hTN}da-TxY^J8hD?9 zUn2zVIG-b==YHRV(vn&?fKM>ES}%aB^#=b0%6ijKRo!|p^4%#@r<7(Z-+f!Z#C-WK zEh;*&qKH|!y+@0Q8RW1fZYIHFPU?r0&aG@Qei$qCNpR9p2fqsN9%r0L5;JqQrV4ZFW*=&3FRe{@{P-Ai)TJMMS}x#gO1 z@RX&WBYQ~Zs6kT)Q#h4VZ4OOz&%-ouT~?zfi#v=1%VB!y4*1sMM`8e(HSC#1+~FNT zaGupz+Ve^c+}8#7JqNGu?W-s&{po%9eCoK~(3Rq6PbR$1lq&kN(L z3|ws>Ckn%P#zB5c=_&xOGPvTe&@IAW2_GepEoQ!kng)Ia<+D_M+Vm)$dRn&pRFV%h zrW)5$aK1P2W!JanE-*= zNNqr5R8J;7`n2KkT}&Lz4LHr3j#GYYA^_b_;h)&>;FrV$v<;6N@r=NRM=1cb+x8+N z*Vyn#fRxzq$QhIS9iTb!S)h{3O>j;RHav8$@GKA#CAXa7=ud&>_%=L#L2S%YR;U;@ zJgy|FZFua$ijs2@>kS(oRIdta8y=Uax+ohS|A(562#=4BW~^<)BWx^lgrkpvpkR^c zXy!F>`O&Yiy#`ZCZsBZfcoc)i*zn}&sm$%$@EFc2OfAywi1KJ6T&l51^mWEf(^!7= zudKsq8jD3=AvRrO!=t}uZG9Ua6~ufS9zPLvtZaCMZ5tlphfLk5|lG}eX&R25#7~@iu+`=g~Ji;k9Ji@*W4{DW!eI>Ui zSkDwCx3F)+Bg(j^b^8`Zsp1&+ZFtaXL)cexQ>v`bKY@06#jxSA3>6h6H)gk#+=v<_ zx9|YaPgQaY62CV#Jf1?u@uB{6h5OmnfenwX>RfD7o!nY@p=ECo^o>@L0z-SW0fRR};3B+^!)tOUbR4_<>4p%_M6nxqSg-^24y< zp_bM_$?X%YO<=?0d}ixK$&IYi;4o}>Ji~;64UZp@OrYd;IxF3?l3Q5m3>zi4Fl=~S z&Gdl{54JERfs$JjV*@3(&#>Bo4G%DDPB>6< z8_m89l-wR5DND(1D`{CuZXaX3Wy9mYnB7ux8%*3%a(jljrR4S$OShEVULf9!l3Tb7 z39?mk3&V!T|FFV=lG|{ieJHt|haysx+%Q#-1kfo8>_K!bR^o^waS*4TB&N z7hvkYGC?^S_=gCa`Awk1e+BOeTM!)<pFwa@V|3^mcjeCVd{7T-~2RXdlN7}lG zY=6|@q}m86<04{P1am`7j=<}i9D$!E8(?w-KHuaBh8IjH1`QLc)J@DwoQnf*7k$qs z-#+8y-b@@v6}oN<2S;voN5QYMJ5OUgIUChC`Kr&EK8ROEF-Xk{<-xxS`Hgiy6J=-j!OeuC^U5dnlR7?1)vm5 zBB|yPsK7=O8v#KcyV6ENU2R-jw$Qc?w8aZ1SB5yC)CW^1DfTlejFAknuHl~z+aJop z$cHkZV2o90V;F%!7O}S>hFK0Uw32EBgJw>sfk&$ln19O1hM2NQGsxi^NzwKnXj=n0 zl?jg2qvNKUDs>b9)jwrW#8~+d^DU}XDLE>X@fU3cMcZ)6a8Ql1v>6p;e?n}{r0D61 z4jzl8F_|<=EMka8YRz`TZUs?gG{ab*Q5uQt4_`&mSL);(qp^>`Ow55sXp}`2zj=BB zC@V9%P0C0Ji_m3i&CWna(zhpKY+_*UStd&Y6CLTcHY`OpZGc3qZzu~PSE5wb8xu^~ z;HfZ%eY9#CYoqMjh~*h$4@TGB*HcsdLOUPY2uq5Q9^ZBiE&i}yRfmcOT#AaU)Z90_ zBBcW=Q#C1uU$lW7F=wM~&H!UIlVzM3B)PRGOb=^7sp(F>&NqHn3;X zZ-->@c_meCZ7ucH>)N0`m%Td?N%5LWk$v#JBu?HoJb%Ya$#-oyT;w?--)GFvbl^b_ z(1WAo(soQscPf5NkC&)&J*DGDK=Lpl(`93atsj)+0noIoJ;^w{65Dk0k{a}-{u4D*E>25~}9aDjGs-!c-r1eRxY6bK$D z6X8q^94&RHNY1Xw5w^?2xGH%@Uve1CJ0*t!9M61!r3QZ3z}W`!S!Vnt2Cg=6lYuuIc(;KM z8Tc&&pEU4C2EJ@yw}HPlklQ$xqx8}M#~NJer2(%txYA1l-fw3__gBB25#0}eZ+1p_ z;~SqB-pELJf`PnAhxi!=D!U@UFEDtmfh`8!VBqb9So}X{;5{b%F$3H9{2`w2aN7=v z$WH}wU9;q<6)i@R+Ls)4Y9jlae)+W}+yQb_*c<7h3^iST+NTKLQdSPnX-Hgi?s z5+(??pz1{E;E&9LGbjJRf#(bQ*eZxjKaldzmALuENe4itW1O|C;wfZ%JCq?M>nz0_ zl=#`zQ9^J4qR_)X=2L^pl6l$ys17jz^>Lgzh2kK*>w1h-RQv$wGG^M(b2f_$8&CRv z{#k4ZxDxZ7hX=yn$DL$w*`bsJw;j{cor)jx_mwVzG?E8ihxG2I$2f_b4P5+;dILW` zt>VO+zMp>)?f4TCu^n8>*lo4%ia78@(+7eb*|oknjV8a|GM zjgx*(-_O4YdgL2eKUCGHm%g8;eWNjk7J<|+eLw%2RlnZ$^PdO3cm{eK5eBx$cW+wt z>uo>36ZER2Qpim&fA<*(pxm+8pUn^iP_jG>KS(r7)cHu4&3?WS=^m9#7Cq9%koxW} zG%AKb`@1{#JKKtKefJOsVXTh@^|OMMSL#jV-Pq5AjkTOzI_uAw6SyO>e`nv&ajoF~ z-Ci?PoRQP01$-8XMdKPQ;VtU@I|_*ve~fYBeC+XiF+_@frbD>e zH(;XSs7T?eKk!c=`%~{5VD0~)?o>-;{XNSo!%zsOYN-NR|8iU6`HpQL-rcbsa68}* zz#V`)0e1rK0^9}oDBz=jkH?MU?9sT#My`%T7+13dybA+@`NMnV;^=cPmPU$sH z@1T~dvBh_9n1Al`Sk+_)#obiK>(wGiB-g7cjg^DGUQOpZsO#0JuGh!)YUdz{u|LQc zUmw@2sqYufZYrWL9y8dXUavL;g&y4XYQvEuxL$1pUiaBuuSWH+Uawc%gb~hgy&83Z zEc=7s=aLhsW}Zo^eW+$qp;g%*{1r;@eAUbkld!Lv$xUfse~@+Y4oWq%3N!D(>(w4+ z8LNsAM_t{_*Q@nxf6(Mhy^;y^X? zLC(5BH8bscH9ncP{lV>|WvOOvAu&re^J-GFR5K?q{y^1C#jaSYnO{e;BXGUimr+|~ zfAD@*J5bF$$m`YaU?VL1gGcasHDwwyP|f@u0HEo24^S!!WjnAssALIt= z9lKtw8pIE{UX5R%yW3C&T(5>6bY|k0a<$qhSQ8woQZ2&!MO~~`&OECy%iLm@>B%i2 z4-yusgyqiYA~wobm@MU3ebBE|YpBtC4t2CD`iMk`?Eo>rrJD$N*l2Q1t6JxN96ULXQIvMT1OKoZY zU25;>U1~BV{qu3H6?lkus=W`a?FEMRa;_(w$^#F1zeoWt2I-X}r9(WznYp?>uJ3B4i+lb(sQJkdXd8ifaJdSpZL*<7f zoM(&Dj&xba5}fJJ5F$t?-gH^VYv?Gpi}VPEUbU46UUi2W+a`|b=q|<2uHT!W7e^TB z^(O0h91Oxlq!B0eOP6)zgBOtck*ARQrRy%fj>xzW0~C52;kWBI9Q1e=%lh>u>llc` z_)NC>?z3pa>pVL&J6T5Et-Dz6huF1Y{~USQ2m76l{He%3=qImAd@P_i&KKcNp`QRr$XHyz8La8T zC)kRM3z3EQqp?l>dW*)M9-gV47+UIY?J@8L1G^1WAJief|9&*xPyP3!>3(>>-j6l_ zFCWVNzgr|wNd z`keT+NPkWz(!PW|FZ~|(syT_x-QoD=?zeEKT1Gv{4EFkbGb<=CCspy0dXPpQHv_$$ zYhSSz$!zB@T?aB%Lo%9r-M#n4CSlG$^ zcLtAH%cSmS@ZY^eT?PW5s-Anvz_sfd9rq$;aF^!&Gl52yMOz&AQWoOfj(`6_kMjWk zADPdO^$zdwJ|V`do6m$g{L=LB9j_V!^X={{j=N_3)bUfHRCw;{iFK!)`wxkUb4UU!qm%cx4Ks&Y}5!*2UKWRt${&*HvfER=q zpwLU-ACJO2g6$$bLZMe}<$>3sZUEaRj_K&q_s3&FPx={E_35SS0@9u{$4H#iuL%KQ z6?|7})h{2RQor>5G0j88{aitNHzLfYH`A(LZ~NnGK#y9~;^^h?J_9A329#z9V#PfS zKS(r7)DuXT&HnfoNSDq2I3JCQqe6Y@0#3jnOq5Dk^|OMMmu(aGZtRaa*8*pk{?6=& zG3>tM{Nz4Z9Px1m;abA|vAk5V8Lp^MMr14M`J%It#->{T?Z@}#s3F?!^P%VqjzNCv z;Ux^5ZXln3hM#TVLIal=c#(ls2Cg>nN&`P@VA8;^5MpikR|B6i;omSg*8zT;#Qy1> z4{xq&X$v^kOCO@YUN-xewR*P!8ok0+hbhGfV3&u;C$ zddJpoCwY6fQ=aVlG>`VmukBtj?A~t0<2&Uj#~GP}vX+8pcmE4~`}vx0XZbU%bpNw| zKc2V?y1V0%2YO#n-UZqn>rJ?-^Av42(#3tt+%M!io4YSVe0lliPL{%xxzkA`r!p-dmbWFUO=awxN|cLmtc7{3r{p2+cw%d} zS9U{t1av}ij2Ys?{AH9k3+ctKy&Q9!}?hRwNc2}#tToQL&P@r+E|(F*iM?9===wV$J}>w;LCQuXK&(id2^HV>A8(fn;A2%) zbFf6&Rh?fDN9~UbDyrs-+>8xgBd8=6>YyfP<otK2_Oc5Iay>4r*x>58Fs7^%erYcLd;DTLoEx48 z5NiB5LUO{NVfc3u;6}o4pwQ5br!oC(Ci(ItdiUX9=<7@!rkYM@n@aE-hJHhN$@hi6 z#{$%>3jL6wIpNpve*nqnzJ_djl}#6Q=G(p>9*(fbG@L;)us$~2GsoS{nxy?_MnBOf8MXb?Nb zeS~@3#e=Jm>YK`c!C+hmx>KcDhK^>=A-we04Kd{umoYQ(!<>+hegRK=&i76qX-5pV`hN=9b6qB{)kJbW!Yl(kN5ZmkpiHWcS^L|3k^= zy7z9Omv4M`>rM3jl&SCLG#d6AiVee)k6*$60CCHQ{|$gluXD`6Faq$7La(!+fvhB_ zxEbDo%uW2*O8kSR%n=31z{%kLiy9U8I5hfSSgnz&$^XJ8ACJ-eGRZlkeuRHH#j4t_ zzBk7if!s&v=tq5Qa+tQ~j=1%>TE*0vqok@feUK!^<1h#~U&0-z#!g&an{ES8bPM_l z2{28*<+|fb&Oi61(4RlGAXKn2)L0UVJvu3I-p0^>IVGV7uDR~P(7@j}GF?7L#(DIeZP<#P|PcDj|gQCmNUow9=AXiE*>bCwC`B`y%9On&=-Rj=pT_4&M zR(G{9j^R7dh`EEu&K>-AWbWWU;ltXSz0ZU`9lj~&#sN3vULV;s5NS@rPqo!4!i+i( z-iiu#Tt!1uRW1J2R5jEg38s*r5b2DQHw#hDZQd*#!;fm);@0AF!8JH*qIElfYEq?% zse?Zo>)|v^T}VjNc{KEub`odXjB9Pfm$|idPAxpTUFrtdsw?U$*0t8JT_uh6Pw!M! zbq63zD|)4Fs#8&3)wX(}39V~`-Lj@eW%ml!;n2KQ^|fqk8x&cfq}NKH$~Z+=Eaec6 z6=#BV^|cOu$fr7nS=b!f=~78*>#jhY`Tln;Zy(jwXR#G?{30gPr+Pa$Ak-fTn>v&- zLH*h$>)a9nVvG#l8kS)ZXZk7?rs$+p9-qFW^|~TXYjZ9BlgcK{H{X-Fd}S63o{?ud8aOFQiCigIAE8y zVoIt0LHipWbS>)n{yYy=SAUkk0Nv!0Qv1TsL%H+?X5Us}BA9DqK+`FyX=-e&I}A;t z%x9%3%a2)a($7j?!~6ATh&u*9uIyaA$hdOdBaZi~a%15M7Pi2DDq@*l(v9mO9nU({ zn?q5<84S{q(G(}%*$5Cn6Yf{Gf<7`bfkGXenOjZwMe&u9^<4*jlk`8)S}!t!dNag zIexjP!vq1i&Pao>%`RGG&ndQcLXa3fnYB8Qx=c*}Grz;^*0Fy$ zg9F$1T{ax0z=d!+YdhW}YP%358TOpuxSWNb@OC`gU2J7w{KwPq$~2sN8gWeDAP6Aw zmNfhmY4|m1`1NV{EonG+n&L?REEtJTZVE@$)*F6KNrGxz{)@MSyiN<) zqfE>yVrm+jnV_cmDh0q6HZ>#hO2r|0{Hb^tIXQ8mW{9AhA#3E~L@ryF>-feQ^>V&FvvRvEb3z*Yk}$4KvH z1Gy#>-)i7P20mgS*G|SiZQzRr{>H#R7#K$XG2Ji&;|7)(IMcud1}-;nrGae*s-qXs z*x8HW&NUR|l{>^wV*jC>;T%uAjPd9xT+KV+ zr3P2?82uK&;cJe$~`zQ;h+KzV)j%tU)1oVXzLAxKXY%kmu{L`{pK= zbZ(FDZvV^v{TERmis9Sid)syR6Mp#iqP-p0%uIG&H!j(+Ke4g%p8-S8;ts4Z1;{rP zatW0W)^R{{V<)-?a)l*b{|j9z59jZs?K;UW!kCdKy{^vs%e%Hue$n+#FZQ-i-s8rM z>`2nIn>4>e8kE&_|B$4ghIfEDkZEi$dOl;_wA?AlZ+`Rad%=DPny%B_33^i@uX+Rh z&=FyO#7uw0o>c7~2Kmso#NLijqP@dAJ=96KEun2#B+!Q6P2P+4FX^J5niFq-1buM- z_V}J{+Y@_|!^$_HUzfFSi|;|67m@#|u5F1u9oyqCwrA|eM6v++VyrXjQUE?TkNNdD z>{0b$ACm^#k}&;rm+7B8$c1u|M~y>bPrDw+)cn+C3&wm;``x2;SQtHqA@&taDdj_chB1ETTJ+(z#2vana2Ff;HBZL73~BHP}VTU?L5XX+3`eO`sUljV*= z1^KSzdz0@_&JSld=4RrRcGed#=9L4FLoE#R#XEgt0p|PA?L~WB&WG*s73#T)#jYI| zi*3SExESLM-N)Ux48xcfpnvkzb2S6{L z_Vfkzm`Ye4t@bC;odx&!}=8W6E+pHJ6^>f2HxNGtg>X{ECe;D(@ z+s!uaZHK*>cDBvkeT!Nn@|_i3sQ=3{ueQg_(PxXy+R_erPjVysmvlHbd7#5*(sc^D zLga#6|wPPk}-ir3F=-iHZTD&jW0a^7$az~-M z$B^}r{K(@RFGOF)(YFUMrqdW5D)$38af- z3}Ve4ZhSH7Qq#dTUq2^v^jPsuMWh{p@$%~vhkc(!dApa`*ghCPcU(cI*Pe9U_GE!- zQ({(oruNu0FsALa7ZgfVw}%pI`=rs#X9sgAPQ8IhvSa7u=Q?)b7hjF%2G7ulvTHj> zIGZ~MqR+Uta_l_p485|s9{S0}`jPkfkk`)s_c$N$b9ZlS-?n>q#~93)?TO}&3FEG7 z5AD9LeFEmn_E=d5?x<(V1weMe%L_3#I?vA;R_i*|3nAgr3 zO0JE5iZ6$LMqKMEM3CmIph-Q5T%;YvHDNdUA#pa|dn?qlLs@z*;;}P)xfpqGv(m@l z*J;aB(jwgx*YDr|L((Me>yU0!BH8h8lkE$s0NcZAo?vL|SYq-q@CSS?NuLHr&)PmF-3yvHM-H zE%B0?TOrd=cz!Vd&h~zXciS?g8)*6<0l!USjG=+FoxkQYX6m%lYTLI=+c*ZCM{j*p z>$Pz1@4$2K#FunZZ-sfol^Z%w0zb7A;h`DZItBm-pSghZep})vL*dWcpI^YXKvyH# z@%rCbpPzI@&<6}hALe-j3%bA`jAXws&9{*zi9W{sdFCyoB`wV9hr@`IIUO@;Uqjk0 zNQ?MqNY|uQWp?1*u|I!U7Z?NV8kcnahVOkmM=axZm9MP}&(ot;zLDL?vkh@O(7(B5 z?Oo(c-r_S~wn(*^?>*GR9fNi`%^0uB>pS6p*eR>+9D;g)k9e8%SVpM%#*P@u`!47) z4)2m@asdY;jg$CEH`C-cZ$w|r?Yt9d7{@gHUI6{!({U4_PS%@gYmkodY_H>7*oApj z!1>PiUageXZhH?)9cz#*yx?FsGT1(rQB&{d^W8!zlSPw@kvgFepZx~cnvbF{pX&h?%9{}1=(I-l#F0Xp{}tV!`{ z<#-|cWu#X9uiWLKp9`tF@Y-^JSe-HyRn3-V*vcke2C zspHzj_27A~?|!uCDa@4%x^V9Ea-{up-M3<$*nwwY$IzELZsW7R+iTB9S*~+yXFhQB z*)tpBT=(w4`yJ0J<`ejY0=^3vzXR(o-hVGMKi6iJF7XuLb$Ca-1fF3O)@LU#A8RD% zI&S0WS^$`j_HJa^u{%3hKky0o>9$iRs!WZ+1zlrM&mI2zMDtGV=o`24hEDc3`x<@Q z#XjCuw5J1W#LJq$+yi>at_>(d)f;J|tgm^-)x0@dKi{`IS9Fa-JNcYR_=EpPeI%vZ z^$7L=-`HNc=f2-!9)wZvE$D;)#52Y8dr_C4Ae`gL{Ean@e zyj_@s1q}=bo&9Jd^P$aM?u29)$Di{dpK}6b>_Xfpk(TA=nsPBeq0@zVB;}fP%}B?& zwI8%Nzc2^7T=cillsUE%>GF`y8;>9OdX~X7E^y|P`M3h?b-CKtE@?^)y{;J!+ zeJP*41p$tIN_$tC^)8QBZsz)<+P(?tn0^q-xfcFyV_pSs`6}kx;*LR}Jz#f|^B~Vf zn`XzUKZy59nET@LE-!u^@sUyNNq&iEf7=v>V@`v=0?(8uYa&j+3S7Jcv6n#qFUI~x znT#aJxu3ZWeAO`#r? z>{@{F+g57E2tI8;VX@rCO_SJf!I)Kxfzz(8srE zq=R*X{UvmEVGivwb7=d}@43%lF7P?|KIoDk_&+?yIpvk@tZ#0d`j*L#m3+1nb?y5< zf;9-?WxK&sA^a?}COOc<3^#A?ZbF?%n`Nl;8S0BB+6$PL<4StF63=y9fPT0EcR2(jLCyOSM#75 zdGbvj@?4x7QYX$e=J^McN9Im5af~VB8dcmf?6tvTq95$JLpsl+-1NC}6T)S#To3=F zvoTlDpDA+%>(hc5#(2si{XAoyczCAuTv_bTm6MR4bA{t~6UI-`F>?j=9m(}j(J4)( zgLf0=il&1z8a3C}XP6ToW>{hq=ER(oIdNtJIB3h9cmwIOnG?^WzPF$}H790Gd|mCo z%CPpK{%$kYkPXR>w~=P2gY&F`F|OqWLt?wZgFZL(TI{3vjzr&g4T(FQZ(&~Ehp=ts z&$+Sqb$Dkk>KIhX_y2Pp#TehBVcYQDe$|a7o(kh|qrKk0Y{VWs7thE9KFJDKkT60c-N`&aTeH- z51QdEo4YxGImaV>51IYZFOhE_+k$TkKKYCDI%kw^?ktOK=`MB9XU?X5!@aHGZ`-@J zaQ(vapMf_*Gq2Ba z64w{ThEY#`QP=P>TMJ`TwsyPm*~5m9*^JlBlCI(C+u`13s(9uNN4isx?vz;a{^7&; z?eIT#ljgYi=I%*`E_o5wuQc)L&atS+D2(&RhHM@d8+~u#?=i>Dxdw9}k?dkV&gpRC z)7>MH*B!OF^UqVBSM%vn!2J`FkKjD{kw0zQ-1+E~C%&;ie%*HPavjg+-`hPQzWJLI z$~Hfk&wMixKkUcIx2Ox_-gN`Yh?FNgJ~0Vv*GJ5Ij%Ns1zaT5cdHpiAc11BpyRg2U z4H~VhYSp5(mbO;D|Norl%-jUT<+X3W_xpY4 zH+TN$Im>hQ{hs@5I7HqK@}2dV<`cx6ar_tbMEYT#mLV_uC*0cp8p>totQXpF)cEcX z*j{1BdO{vBiyZ)dYCHn{9(b@`^r0E;1|i9Yd`F* z2lU;K^H5Igzj?O9zIr;!i1mhboAvLg)IXdb-&Y!o{6>0)s(!fOa0BxE1LTPv$NH%E zQrDr49Th#+b-U=kOQ^Lii+&Mi8~hr+x*X>m#+>**<|pgeg1;a8lV?`z;iFx)w6p#_ zbphsr_ikzbbo!R|xv;}r>)j}Ze~|U+qQms#rQZPd1rZL$hFwp?o@Vs#59sipgjrPc z)d%mIu4#Xx7c3T=b-=f2=(V8 zD)pkY@0Q+pqS&VKc(^Y&Y(2(9ac7RLf>y^-^DU_5?UX$LxmW3<{ZYm8L_6Vgc5G;-Njnn zqa0?Kd!qgq9QLArML*dV1(6$TkRQE5h$q(FT-(qbdrmiJpTsSDzrAi3N`oUm~;8>(eKNBFU{+EoE5EZpAxtpb-VR& z&rH-O7O%xmf~&pVtQYHq*svt#cvZGFIhO!qh2?Eu&A%?NAOVdPyD;*Il^*0yw{ z6=$%}e+;`hXs*ZlO$Bn^%D8%wFL0Z}wnTFe&5{3JtpAY4GZ`M++fAqkcIairgEaYJ z?gqXc%}~etVJ-M>_NiOg_a9mcJ<%IlbkIir-Hv$)#&fJ2O50~lfO((-4XFHe8W|ps%rEN8Gy)+qVpW3I#t#h3~>qMk|sukEYH-tPIlkv-`7WTAJS0*{u z+C$eM4(!ijkmu~jzI7CB6YG)d&=(}2oOg}?W!sH+Z)p#pPc(I(ZG&!0(O%dtTG3AG z;4go|LDY-MhgKj>4`pv`Yr%f|9e9o{oOEbB_CwiUqaMA!v7qcwtA#NF{)*v`=6Jp2 z^=`nOdzio0>j`v^`bM+u3HuMCY@SUxeM|c~guC6^gt_7yNT-9dh5eY1_QDgS%; z!|M?rj&V5iI5>O42L2HTeKQrlyJjo7D|)yA`@2i17D;4u2d*Xf7x?KwXC(6(R! z&TU@8K8`-4^1v_p5{!)t4s-8`X=S>z5a$5uw+DR*@6z2F+SV4pc+S4!VU`p7!XXFR zc8!0tjb|tJ*vW^kL%y)zWnaiVO#~12O}(O!x6s@DaORP)p}o&tI1@!1cJuA~*|Avi zX4pr(TiQ8qHDfBxSRM(V9eZ{U%@686(U?aEbl-$UfieM&_3}r)J4?eV)O-Z z3CI(aV?fPqCLemRU?c2U9vj;p452+{mmTDL6{ zYvu{dk9~-V_g|pThB?dc2=rLjhSS9IL|=AzE6nKk6wugbe3$zWMqxZwS46WBL98v+ zJtMwjg?zAf;F;pVJ{H!5T=&&rUC1?HDb{_-sE4U2_eEF-a*p81Za(xh>Iuh{F@X&) z`e2_B*zoJmklxfioIkwLPP+io9k+;Q8XMX%{;RySL!Y!WFO*$iLp$??`yH5{wvp-u7Oh4eg~Fs85e=IMfGvwsEfeN)g9f zkT(g7n146D-G9%9_9Q$3JXXRh?Z-m)p-{o(LtW5^#5E&s8}YQ_$!=|%j5~Mi!+kgl zi3;7&HWuMpp?QcyYfo?Jr8eYMY$!Ale&-zKcwkwttV2I=t!;hO9tveEbIc!aUX^Vf zDntHEwF-M8-LY&_Sof>_g|5&CpkIx-y&Y;$^Td1MW(E31JG+(RcdWgC!=W8n7?%+a z&XUv_OphzDhP2Q=HeS$q&WBsRcRtoix1^xeq<-<^cIVcA8NVlU^Hq2?A)a)<5xD3} z#Q9|5p3E+xQ=ViE|Kj{P8NaV;_wNXQ|LJ(Y7-Iv^{;-xnoty6AJ`2{U@2)_*wi5PY zZm#Pt*GsH7GhoMcq78fY5oj~$S2v(7p^c+Y@dwv~3@(4Gc&?596O8=%{5(k8eU{fP5RydUz*?)0;L%8PBQ#^bCD<6{VY zqk}!R=z`UUX06(A=t;~U9vR8KzqN<4pL!U6+P1;(4m{iOu+KsNg}!^!+tH!L2c13Z z4|0yey+f?i`*F=lIyG=>@3=j9LTj+LTHQMoU^v&mUGw|M`pQoD1nWq&USRn*GKxoH4TRuv$6BTi51yZN<4_^P$+m*B)fQv<7orEIAJ0{IHGf@j=WrnYX6T z`55hy`Ivt;%Aob)?OwIdFux6R%xCj4CWSC2Iq-}AWLNCQDx~9r!+5Ryb?y=D$kKaX z=uG;tFERbcMaX0J71s)0O_aXnIWb!cWIpf6d{OmDbg%mul*`8Hhw@?1u+SI2?(VyA z4j;?%WZ$ID*9-8j3&se0?4m=Q4{(o*^8l`qPI+FJJ;yM941+OhM||60#> zvHGbqjcZ%6Uch{nZes%2D;vK-oh5F-{Dy9k&#yC$40|qkG0)HE-VEb{_mXVbosuql zdRV)pAbrO+U>^AZ|Sou8pUA;$4cinK$#G?iO9J=Hemn`}DxL@D>t7Fp#E!+E2wuxWh$gL08 zkGSW@Pu*G-HdA)qpFeZjby*$FEGw}4@xGnStn6Fjjx)2Kjym3~)dRCV*vh6lDhTbK zV-Y>Anvbmt1wQ@NleZ_XKAiTuEziIBwD--sUyChW^W2N?e)MvWq0`?=8=W?kyXu~l2^ac5%2|Ea`+a^_vFpaMQ-3`0!C&1t;gX*}bni9GD<4nKyRa>P z-{Uua_YcoZ$Upn+mAgid=(_h`pFUsQ(*3giKYDAz+$ZkYIwQMh@slr&DC>J@-l;!* z;ll~BuijaIc89;C6MnhBv-&5R?`Ow8J$Cfikz)ss?K#$b%sOU$W_=2gw@0jl)*h=F zmxH?73DG_9KQz9l--*Xn1&Dq3`TgVlxKZZ*{Easv4n9iEA!`uV98Se&ZOlh-Vj>^G z!9`B<37jtG6F9N%@2C1BZpWbii{s~YEURnBuj<5i{Hkg<^9`N^aTcXNzSF(8H4I}` zkKTyOusB8wQ(HnW{jHvFSRx~n?1aDk(?1eN+)fIAIwxvaocYSnz$oOY7S8BFQU}Qw zbC7=|18y(V)wg=GVLTX~M4}^I4T%-9I|hwJ`OJ=RNQ#iSrxM|gkHfk?8H5N$xDOkE zG?)(qCF}124cFfTN-^I98bJ*{ycP>!sA-W;38kA)2?gDe=ZAtqk+fP?MkEcEm1$C7 zStH%P^aqBrBn|QnBkP3#>LQ!6>JZVw(-dKyjq%lf919mCzVcsm5;K_plVR+Q^8Ox9 zd{I%~!)ncluiUI0MK=Sh9*Sv!73X#S0CP{z-+=IL!OC02 zSI!43im$w%wkZ=pfn|Kr5`3>m^Y{933=*b%{&FTToRoPhjd9sVT3272J8UH0AT}V5p-s73Y5f z@sEhFAh+MpJ$KU1LCv)l`4~mGpO}rUF*Na0GuhJS*{)#dJA^M$FU4 z8JDS~oWy3^znPZjG8rH}{x?W|f%r-al2+m?RnCURS3bxnoUfzc_2=85F1S(!>hm)$ zp06an@^nOm;wwK!ZSs`=LL&cT#Pc}um8w|kM#8G=DTu={guK9WcYIO3<@up_jE6WY z&pi^ij^ZnKljVK%=t+$8k0gGM_{xXq^2eG0=| z(>g^Z`9EZyKKdO+CHwJ0k>%NSm!eYRiFV8=Syr0!nf&Xe2#4nTSBC*h4N_^o7m42KM0}+~zY+13nG81~zEU-)i1^Ah+BSF^n3BYKz1cF|;wwKTFPHer0SwtC zzH%IEkxP7KG5sGezOn~n=n`L<25(AyrRo5D_EN?I*}ak2MHL~W%l2N+&|>Y&m8}ou zc_wt-yy^HqWjFqAN46;Om2WW`5%HBh80#dPPCSXxy{}>J4Yesr5G~a5QNu@dyB^E? zES=nK5#S zuN=m}T;eMqroBsiWfIfp5?}cjmf6X~SH1)Wz8D{l<}9{Rm76h^FU3utYMnY8^bv@d z?EL@(tn-j&S^J6gp2dn>X8EXL$EElTuzakNECQkX+z9#zs%l@hRU^J~5EFfghhbX2 zt@sbeqz}$qK-)dpyisXr+0DyAyx-Hxd@zh*^1L$Njep-;_+#BN_2P#J2;wV$06$)@ z_f4b`R22;E=-3;&V0XzL@27lQTZUrG|1o^qG8B8`8!of#(G13u=!dhIY25vMk@7Fb-Gi zILnzsujBc3j?{hTXrU!z;7`d*Eg@bLp_H_|nb3u)9NH zcW=YpB4*IX+8y2S8w@(tf6?w{Sp!bTqW({3Fk5%@?J%$CZD3wI8G<4vVmJ=%zm2rrSj{z60s)QjblZ3Dm3fEp??&^o2UVmg@YP%hX<`T`gd0 zMXy3$=3zBs$!|djzhWue;<3q{lG0dG$dVtpl6tGjo&g8o;Uy;ZZjVjJoL7m5yIdZA z81@j?)w0IY(GHJI$CcUx%IG0i8bm*vW%01_8<+>bm(u+Qx-95-TiE~mF8`uesr$3` zzhKCI7UMrWo8iM6a1(e@aIhWYC?UBVfEjW0IgT!&#C^2(IVx`yr!I~vtp(PAm*M*v zmfT>+rq5|?3Cee%gYO|O--%)01F^yIeKJ4GvFVdjJ?;BU@tt%Et;Z?fi3zmUdix^g z$k|~|mEd$8v%kQx$z@-d(@fz+_p`*k<~rNORqO4GtN~lW>0PF%+_A~!y)dUT7pDr% zX%1^sr8Y;sJP5yUu?8)3Z2Id(y~>TtTz;2pzq9Fgg*Hcx8;wf|6BzEd9h?5w^x^Q0sEJ|I)qsR@tI4}qxB)t`y^-`*{5`X z3kKCULU4_?DrDaL!cim5CTmbJQv0-acHSTekP*FV70>86UStJcLac6QOx|}?Ps~{D z#At(ocP2z8$B9o}l(cpgF2I{8`eFxRn`qM8{$Fpo=a0LrH1k zCT4_C^AlGyvxS-!+%#R(9qqo{)=t%T(3$1 zBfpu0p~~8Gyt+9uYej#)j{G@Zi99lGns%i+a?#IET*PQh_nPcP>~qwBYR_Zc$@Qw% zT&Sa$rx~1Q1%8g`OksXL+(ku)QQ3%5OxNfgT~u^bfBJ~lpEsxxMg3Fk@4->w5DXZW z{rfI?@L~nx`Ti!EU80$h(JdUnRP(48W&_EfUKk{9bf8k)|`#U;q^9Bd#Sg82}%a(!Lcf4GH+vg2VbJqIk_2hk#Rfu`xy z6tX}?KX*_u0t|N1B^4O7*hN{)fspp2MlR8-aXLfi*{sB8Sx+-HRWJmRK$Ymyf-r~N zMSXFX{m@$jjv;1UnZ0jE*^E&f@>9p^k5Rf@87t8jvIUD?+RGoKRFykl#rK^k9p7XX zUqktrckf22%1|)08s9ViQxr3NP<0cUv)0>lK_MWa3Wtnf_9aCZpz00Dq<&2FR_Lt( zyKx5MWBnl&WdMEVOfXTQ$&DlBPswSVK{fyh7 z(xn)>{SPP`E^RosA28??%fegzmOUxnCi6X@kyzCQPt;R5>4Jrl5}FvV(tE+6YJ@jA zp1Ez$A5@KwTZlq7z$x*%JJ6lgRLyU$b^4;daDFj^-5hIk<;-ATPEtYhtF-n_v1$ux zQ`SJRzbTecjh=mjcg_vo!HKG@Vp;Icrj6dg-($nLN$4BBgP&wnZi;0P&Zf=Y!56Yg ze^1f&+<^_3BEP;DTdr(dz0AGutJRvb>1k&WdZe3U86VUZ6rSkSIP(J)0#-Ummgr}* zz(hYQaXIHcwb{eYbKj`wU*g|}TXGkv1-;d?T-iDQ7d9@PW7vbtd6C5UA3nKYO z{H zh&Vt>AN+YXrKTj&Y;Hv)Z6jL&*P=u3r*f2XdZ-yuPdcKiob)16_ULRF80y^-b9FlqvQi1 zl3(ZFTlgEE%)}mn4)Oa3VQz{S!5|_Dgas-60M4dXJ2eTK1K@&VlCz=RkIKc`n4AkD zc`pBo_*aF$v`g`qfWEpV5hg7(zK(yJ@t1xF{_NSw4*-TwW9Yk}b2c^GDNiZQK`Z5e z((JKPehZCVkkStDZ0eKpA<5Yz{s~Bl>JL3)Ntm6&@A}zuQ2dr=JYfXy*9Yh%(Aq-?cuZYUv!+UnEVS z`CLDn_E2py&@o%!i`6&R6Lf+8EJd#(AL%AW2Q&7O3cCR&#i2iOW2d zteX9qQ;{k=CNA^OOyq<9%xX5s%i}W7r1=GLRxk|(v7BM`ir2Z=Q}tdU!{UiDP{ zAba}W%AP&_DQZcqQ=s)stLVxt`_y1iN2?df7)*~TWpZN&0|Me*A^uM7W zv#(~{TjOkY_iTSWHpF0rvfUELYKv}XAil8)iv2e_m_exBx5w$OQo7tb;&hjL(4z;x zJLA~pCN;Atx5lx;r!lOL!Tv3Dhsj;g?2~)pFMS~X>;=gwfZ>baE@d=y&Za${l!;2y z>PcZwgXZi>IUgEyCnbb=DRltPrlge1NnVh06=8nL4azi-vPEJ4lpoP}Zpy=i!jva8 zJf|%Cq`XRUUJ75T@@(pr@@H-Pcf#xxFFH5$oACh8ri7G!3gc2vSLjR0R5&JeJg{jf z`p9#j;hT1;GoayXf2rp|!}s}8FM@_IFQ%45!#4_3YoVD&&85cb3TXPkSK9ZWDTLKL zX4G9E(tm)%p;H5u9=^Z-qY3cu>iwE!!NAh^Pn+0KvD|c@8Hl7^07cz zG+M`wDpQ?+uuB`MY^|(D#;1~*SjdbNkK9I^l;-QTr`?)fnH|*RoEET*6N9!Q&;`)GhV|rG+d-@3_OUGgmut z>G*rnO8;3}MhG%ZYEqmjIq4bs6|?b|nvpRpZ>Qr=cP>kJV$+@FfKLk7z5QMr{)_g&7S9H(23(<{9t*QqXVsl3xUX!!!WMhBhc=`E$9p!1_sbJCq| zyV9J7oIBGB(!e3tDZI@Yl#`nqtX%6f<*d!kWiWlxovL)_)O6?7id~r*!63~Lfu-qA zm-M{Q;!wzGOm|L84>|P^%aUG^24O4CCFPY)*BqxYC)4SY<6M>l3nVHwJ&%7gTAVY= zgTYW~D0OCjMLrT7%CAg=!SohqSq?%9&Tx@E(=&tl!Q9kP#Z;sxGdLrdyLPJ6Gbfne z;>4Bnzf1YZU@(7eunHLf3m4HlhnaL~I`b;X7{}$L7UZXeQbq^Y23w}5)CS8S!Z^)| zU4yjbI+y0qeah^X++bBzFfC(RS_YW6w4{cD=Z1p8O5pS$qU*$#J2P{f%yef)jx!1| zSyvgHo|~3gJ!)D8QoxKM+hAHIvLbE`bk3#a1&dR&f|bypGmDN#I?u(f$#Pa8MZpYk zjZLqfo{NZ_la`n7^hL4G%1N764QibZX0|BcOut-%RI^OLKC5?sQ8ZKJuuOkdu!(G2|m;d2j^M<-|e|4l~N1 zvoifrImSkPM2L5&#KJLB(JPo{4u{!C4PHFIg9odJKcE;Qh7Gp>A^gw zFlUKVP(Cx5mg`I`zdALP77C^Wok8i&m>lPZRpk{y=Zu_SZZMdVnHqGea*&^?87aZg zOl1}t?NsDs&@2tv*5=E}nf0oZk?zbYZ>cIt%gng=3W)d_mAfREpK)L_3^BXiVan`lVS${K}uT~iTC&CJbAnGu|x#CLCi~1 zL#d;tjZQoe_77R%+TUYofjMJ912`o%NDe}96eBaup&3NVr{OoVog>? zZeC?>b%oPI1=3L2$ssysEd-`;zce=CY zirbv7XqmgvM`Wa5Rkp<$d|OH8Ip`?zE1N^XEumn(bLlSRQZVGKUbQQiUi}E=h@Io? zUzv{X6vT5Y)29bpozvHp=G|U#)wyVq!6g^#z6sesJ-B6h<;5v8Lb>alwX4=tprp~E zu3=VPihRmx&U3n#`z~-UD@RVU2{`xRxa5;7%6&I#9_xa4sUkw3h`hPl&RLs}CY9lQ z=)J9C?b?E1#jF(7bTS^#3mpliI-jh_$!pF_omnxXG?e+X>A_G18huWSa~acto>e8x zdEHy?tY2A<=9Zb3n=w5jmAy=c^T8EsewOzba&O5MSEgi^<@&}~I(4r&J(0iVtQFPi z$X@4=b=%rjXMMw(%G8n9Iz2zSW@|NaQjno6gt{5Q_nH5K`P_pNA|7RtzUs&8{nEr*Y*Do{YqJ6(NGv!Js$kQ6w5 z({nY;G*t6*GKU8T>adta_~=Fr#<|W?REKcLdu>7)Vt{r=sY}}h*GV@lZ0nVJmi_RU}i@{s8&2D zGT#StZt6^l_b#Fr6E4sFvtW+Jw^E$ez5(5o__*urb)FW-GcO)zN8xz!EWW@+^Q`@2 z(8fQ{$hURyKc$0zo^ZR<#RF{mHSyv3bdrnac{Af}?0NRgd@wXmf=Qe7^K3U8G}6hp zWy9t?sU7DsH~x9+EYi*Em;uuZp@ddR$H`;Wh(*!e~(HLEDVSD6@~ef3SAfKY+yI zG0N=vZcEhm#@y^Baf8JjuGu=tLR_Zc>5nIXCk{^n9t)2b&(2iK`V`Ni;dm7Sk5RQN z8tSa#vSn3GR&8BFO(|s5)D$nfthCW;sw*2c(rR3~xV*Z;YN%NZ7Y!w~kYi(&!LY_^ zs9RcVK`2crM9DSQ)s|UBmz6Zt)htG~6*oa{VO=eS)D$&jjj_tW7tF0PY|}PcU<=+> zQCUL+r292pvb3%Vd0kaeTU@O~6V`G^rGxaoqN1A8s`7HWf}}wRWYj|Sipr~tD;llR zvXc5HM5=OWeTG$2iZ0!%p^a6yvpET`BVrnQj#!Z<>$~HH#ZjH7_e^l=zg@EUm7wkVg&lTvuBWP=6gVv8<}9 zk?&a*6*nr$i7Ky)iW<+Eo;MX`hZ1inV<8t6DU*`ohKA)<5#&Td(h)@tRn-tFe8`f>n^G`wcK#fQ(5yfOf$Q3GtF*Wp8IDY0W;C({ z(9UqQ-&j%0>Qz*7X%od;szMxNLF{7*qSR1@dc?G{LNR-pdJ93JULq%v)G~@rVtVRS zUS3*Uy|fI5)n&yiP!RQ_P}v$9>X5lbbMufZR+-dO<{tQ((yUn=DOx2vvbOB9#mf;O zl2BB>l#xO4GsDFPvjgnDx=>Il{GugRT?2U6mZFX{A*yIQl}L8oV#K(yva~is1F}Mk zd_qNI{-C%@t4cX%L7S?()T&i^j8X#&R7uqQqKe|0nqt+QpmWs)Rz+l8M#f01XmK%i zxR;kT>hgjZtFp4{D##ZuE3LW|NmaEI?5k8Db!^xwzf5*fUZt{HfhvdiTQwP@Ysfq^ z%UW7eR|_79c3E*PZUWTTH`JA~os^>kKy_}YE@nU|w;Cm56)^;@szp^JWSv?~I2Hyo z$(ZA-G3rJsI~T-2C0=>0L1R9FBv0iV4Hd;}HK~Zy)-fNDZdL10tbQ{yP3;n$+ zd2MAiutk*BsD7)IePxEFAk!t4RE0ve=q>8d0qKg4)>}eBvM4guaiJ|NW-nV*vb3QA zn_fmn@rOr^y*zEJgGvDH*lueudBmgQx|sK0f!MT}}uaRWL$on-bX!N!t?;--?y zV14n@>R@IlG%9V@$ce$mhLRwvaCOz84B}J-a zbhIcXRQC@{YnL{bm0D%Gmj+YSR+TKX>d{%n+L1|8339EXj@^b4)5@r#!$wv_#?~^G zPi4p;v`lcSMzJ?owac0yb`xz}HG<2KpU6DjeK+y-EL5-2S(0?r^TjpA=+lcz8|#WH z(LthsYgBb$#c7O;9;!8~7Bw1OQA1fxG5q5Fh581x>58%@M zi{ZHe3Q;w`3idDP&)5RlnKdedG7fIlE7(c6`Un;*yW8?wRcMS|S!qB;2)zKsi&k?u zC{yg%d{GFPY#?%t#b`n0wQ4_9wMlgCx)Q6dtpW8{4W~u5DpI|q&H5dgLz=mF&ld0 z2U_dtIhM81<QO53i&~AROfnNg57(Xoi!u;-n-uQvmS`B_XsS#!D9s*50>jTx;#W>Vs z28*H{pT`Bo*qU?kmy-+oZMYj~Jh^!A%|2K5Tld1Bu#x>?m0gFpVov}sC#ii1 z<{#6~JaO~G^whe7o<+H|#z>*t{C`3&tSLLTNy>fYa-MoGB0`#a9uVq!>!<@(b)+cn z13(@^qG>7jrvrIx$vmKcbPF!!4VSAn^jf2#$tHh1bbmHz9*hnFlAf#`sy!hOOmc+Q zV5?hF+%Muod$woRojjp=%FI21B%#?pTU~S+^!xBk!$beoK(psXx)F3X;`WBvF9(`E zF50gKZT7fGGra}jg14{Yl&w@64T%=zBZT7fGvrU-2Ez)m- zHhWs64}s>}?%cy7{WfT`S4H{*(0qHFds3u70h)a%(*FW&_MJ#afsff^A{|Rw_LN97 zzkNOMr<85+DzVv5qCNAQv{u@Ee^zRxY<1#o_Lhu$(#HNY@H2Zxw0A4YJ!be@S-F(b z;c*+r(6m3oMxm6pe~&jM}s!bnf) z^fcVGqT+i0=&-G>! zCZBrFuNP8!?k;oLq++#{s=9969Kq)ATwkxc(EdtT@1D3K%+V{=QYcI8m^EP zW@IhRy&*+$Cj~o20$3GrxeZVoS$bznEoZqF#*UTRSTPfPxaAZ&yq(3}2eoEas|VTF z3NJH?ii*pM7grT6#flvhdb7_M-eYX6$0h=Ib0%pS&R79Z1a#3PCD=KHKTP50& zveSK=2mMze9^yQ~1%mt0_WPqVWt;SPp+{RH^6lFGxt3Lm2_~@~`uCh@4? zr-IfnZSEEH3C0N~2=)@}BiLUsAebbWESM@763h}DBREblTX2ftG{HQ<0>MJTd4dZB z7YUXMRti=N)(bWXE)!fS*euv0c(veq!Ht493T_s>MX*(HtKdC?_X}=fKB7>%*TgL!)#F-Ff7D_iE2JM6?aHT1Ho2dmLR$)MNbk$ z*QMx%f|Y_Tg5MUrS@0ggp9=03{FUJAg6)Fu3;Hk~GCrpWa{MOE@AVKlz7p#Nx#l3f zUhrnYdjua5+$Z>=V7uT4f}aZdkvHVmPjI~8xq@>AO9U?wyi@Q;f{zM5FZiaQlC@6$ z98Z9Jub;?sNaAck&Y?-y3a%8~A-GrYcY^N;@|Fqx`MerW6&x>kK=3z$ZwvlSFgi;6 zgOC8lhwprn-$=npg1mf3^F@Mnf>#J`5L6#2nrc};5_*^5GlH)PzAboE(1!|4{(S_4 zf)fO%37#)lE_kKjcLaG8fqZ@}_)Ebz1>X}?U)X@VShN+o?<<%rc!uCPf^!8+1ses| z3f>@ir{F_^PYS*ycv#Tu)8S?bP8M7#c(q`=AkSkN&%X%%L$C|h9yC8yaDrgI;Dv%! zf|m)d6Wl8Jkl;&#za^p$|6Z_5tPZC;5$3%Gr;7P3q2~%N7xUFZuM@mg%s~;hrmag_v&={JxmqD)?(LABIH~`HUpO{~3bKVt%vWJ%W!2J|+0J z;KxMxKPq@OHq6MkRPZvvwM4kPO7I~u-zD@up$`cCvf#&J-jnMF@I8$PK7#}^#XMVZ zf#5BIcM{?L9-)6C^rJ#QCG<0b$vt&AqXo|M#yiv;9Mg3ULg2@m_H))F2U~D zh^4=wf*C~k8zs0v%&P@g3a%IYSa2{lILRkXFjsIk5&X^<`XZrAguYnl%LFeM{629C z_8TzKqW`l5rwHZ?o+nr&SS#2dcqI{hb_o4*!6yX|3ce-i?W^6-BO=_@M5ODxg0~C) zSnw&qzF5D~-DJU;f)@&2DR{l$&4PCb-Y2+SaF^gO1f#GnR^=o(N$_&PI|ctBI2`L` zx<5m3G7;%HSMYiMf{zOB6?{(cSAqwL z;QuKR@f(EAcH%t2Qo(w`m4Y`4whBHXxL@!!!M6mv;XsRgPZJy_I8tze;JJb`1@##eb-B;+-g+5E@X+kd)`eLD13B6wE z+l78W=qH6fAoO6)$5CGB9xTs9S+9Uj5;~a(I!ov=LZfJu`)NWi5PFf&O+qgddcDva zg}z1TR-tzby+`Q%LLU(N4WSPT{i)E{`cv`aUOVHTAT-YwNC$*Y6`E%XOwTx>vxVmV zBi$DWy+G(iLN^J$OlY+Zn#fOZYJRtfeXG#-3%yNfwJw^7b3vis5c`8de=0OKvsL_( zh!e4gFLa*JJhNl`D~0Bn0qLuSUN7|hLT?j#zt9JSJ|gtTLigf&2I2J~!fcGtO=z`lf<31DmIr?ZG2AP@e1C&4B&F8{160^v4UHKc3T2KB1V-^s z{xd?M(WA2hfy_}M{8KH8T{SiD9DH#ZoV2p7WGh19dC+53d}?Rv-z#kbAR%j)rG$TDg39=!FffyaVve2)K8wCmf#{>aCU9ru@oz2-Z? zQGWREf%rt5uM5Y7zX1%-`WwJL`CKr*0gSUf`H-+8Vk2J%#))j?6V>Lc(dpKNIQ6w; zS46BwkWOSalvia$s?uZi4z#i2u{;-3cOL)xD;I3f_CjYKfL2t@HSgv4v*&oPB+Ry= zSX==-(VJ*62mR8m(A-EO-lmqH?F7-4`Yw1s1lq08#um`D8%-@gUn%lN#d*8h3urpb zX5fsXz0g_(@K{CsxBrQ6V53|aKRou=d?#pmW3U*nXjH{DUujF~kAkIwa7&<(~@ytuBR$-0}mY846S z`s0zZflez^KBVxXCf+*zx^F<~)T-B^RC6k^^t*#PCOQ^h{q4Tex}u4-6=l_P&Y%7t zczp&hvo%@i>OHdb#kdolURt(zX+=@-;>8VRms;r!Wz}MWLzOx=U0sE@>0I{Z4aGGb z4CKR_w4?i;XgNXTktm5Mgx$k=P6h-If)Fhv{IvX5cY?ptbgc zSWAs4W0wQk&F^Xu^iO{5s0_c0pf`S?Rqv>9&S8|vzxgn5^V89-@c{S*s1arC*uS~??F7M2mGQ%tK$ze4(9=%{ z4;A+@Ta`}#(T)#$RKuG#+^=-!i?L%L1gmi?Dhbna8$Zl;LM+gKqOLeow_66Ncvwk+i8Dn!BTF zstJkqF{;mxn7Biyy}QInv*aV{i_guO6A|TD-BljZBpDCSsj-|6k0Yer!xc`F-QBmI zJE5_0{Q2Co1y~NCJ&=6`F`bC4$`qU^h-Hp4zd&%IAlnH2trTRPBz=$IPl(|6kYKeS z>wpJY)<@`6p;?#do_*kn=YX|K8q5SR@Wpe$Gcq#2^c?W%9tafGlKn1o`V_RdP#y>vT?awDQq)<_i zrNQ1=WNDm7&O_NvK$Zsk9g(GREu4rfjR&EnER7)Wb7X1oQ_@CWhR;78{h^`a{G(iU zUtX5R9QdLv4Sr4~B1?m>+eBn()YC3Yr_twEuX2piRGdGaagZ-~C-~1~9L~^oz5KV4 zOIVi12eccj?fU!G0{Ki$1+*-UrxA>jrJ)WR!?HBSFocLK4Rx{@k)^SQTyvghz9-`T zs&6cq><86lE&c{s3C*C7!i|!pp>j4XOJgV*pRc3f^?#p{4a?GCTs&V%md0F!M_C$w zr_Zn~jT;$8g@l1mmR-w;@)DybB1@wcZoYynjjPBqEK7reI6hC7#wY|9k)`n=Lk-K) zP#X$iSsFLcWmuL5$JvN1jbrrpHDqaAM=pDR#Q(%>KYH2Em8GE$SD(?Dl;^Lf%V#xJ z;O7@rBC<3NFtsl{K^KXTrNMRQmyo3qjq?A$OP0nZ$Q30^<2oiUB1>aFbIVA}@b)gE zts#BAm3RU}#`O-+aYUBJi_DCOERA|LjEF3aVmgk<(tumbB}?NLIzGokX#k$YalNDI zI3i16WGO39VhSG+M~a zB}-!uxw&L%1ZaP}EDa74Zdn?4z?qVzp*nzwERBEBdl!2!0b(oECg4eAY5W1PRkAb= zGTISY8m}<8h%Alk$nGSvG+tpMT(UIOI}8z78c)%CM3%F!3Ca6mWH1(iOABJ z$)btK(s-1PBeFEOIC06+c!GW-vNTfYHzG@8C><~I#G*<#kYUt&8EKa+jed-mOP0oB za&yVj@YCKUOGC{zU9vQmF*uhjjbG9K$z*BVf^7RLvNRrNgd?&vC>7yr$m~m#SsH5*Lp%Dk9L#m>8Az->R@F=-Lwzd2Uf5rK7IqZB3Tw~p&&yc$s8Ynvp3|Qf z--MVwpaBEi6c;9}ztB5;JE^i`y7Wq|NlJ^BC5Y}4tBV_$EJ50`4I zmzI`&<6rZt;&SA_@inh8cK(R;%;~vRCAnDc*y@`X?tMj0S&ibvH2lqry!eEEpiz`i z^H8jRLSrhzZq^dq;HNs#9-^7r(%yBQ*4z-r9lpq2V10Kpa?erU(hXlf$F;`d>c~~e zBE5wucLnuzR8E^CcYe)nYWHn-*BxVZ16*!B%SG@ab-|E#pw;!`@Qr2OsyCOx)y+Y3 zeKT?=n|G?!JxzT}@c66l`lh8^_wIDTxx?MauUFkvRmBdgt{wW$DlaIj`<>yth8?a& zhA$%Pi}c}(^Xi6e_?oS`QQ7e#ySkdHFPf?g%ql>Ar8w+Ue{rBFeC=0VWG>P-Z_Ry6 zbC>_C@BW&ahtCqo)nEBafQkw)y(^8n?H|6GZ|;K@*jXYEh|$8!dyV@ zcs=+87opAl`tY|2)D3XCTdt$f@!oim`%)=hMK9GCM7hJPJ{6!+qOT^)4Pm*lOADRO z8s4L2wQ{wm<1Y%!6;)M4oziBmkhAuiF94WtD#$k~VCA}Ti;U}Zv0q($HD6HBaaOUC z+uiyutoa~=vgb|m$n9nI9RglB2W!{2E!@|R)mH{ogLYpdXO^+Dn*^(_0ylgvZVAe(*E-jP0v1zeecY{3uLmAk2(1cHDb)^V{u;AH0P5T?M`I zLmnT%GC++eW49Kxo8LZH{NiC}(t8v1^uzL_!V#=>k4YqG+VQzv5TMi}I5-$%2q4{1 z-G>KtrCt&E`vC4vl-|zg4!l#h%Xk7BendSl(w1XRL0nrbd*5n&vJOrjliw7k1)~4f(MqMU>O&)I#5` zb=xy1B8uk@Zsp+Qp_>>K0D10!Yziy-<{5%fikq4D;7|hB-@;P^8uObp`0$Z>N2ou7 zVmdr1N2dAMjIO-GuMjIO-Gu4e}d_N_!b4I5|97lwCu3&)m8s;2hd8Whl?@7-CtBV&meueYE%#m4RaBK+m z#qWf%PC|vx1NlxEYSLkx?cI(ReaK2^J9OfAzi_4(@Ar?#yuyhb?|CEVQEplD5K-&+ z&h(#UM7yi)avd=8LgxgLC$#p%`14$WKW~hqPo+E@DZNo1cD%kQvGeel*7kYsR4$p_ z=v+OwX+zcZ#ds<}`C_~_#|t~#9u1&k#zXc6#7&H+QFOr#-dQ&wBRDDXSQqmDo7v6L zA7a8GwkpAnFI??`jIat6`|@2H(SfEgr>BnVe_`gno>Q>{4hy9aq}n zh4WWm42RcpYIPZ58#X^~F?)h#Ys7B{Z(naeLT7f+m+^) z<`LrvXxtIi*dTWNW(qA?r#a_mJ~Q8$cVH1#1;{*(ghm;DhBoLn|KfF$J0=Xz9U^H{ zJ2dy>*G(}U$~clrS}z) zILm&gE8HV(yVN^e;1L(PwOzfVRnU6a z&Tc(SyFFI(+uf{ii2lokhr|U?*ey1xs`^JMY1$P}x2yHkNn=tF3Kd|>;wB@`L zvNpW^A>Q#i;rm@Dm<#r_?Vf%=r+w&i<*g^o0LNf977bD+gd}55!DLd;57BL%cn-K82QC=0>KJ>{v0SWL?6PQrmg%1BEFiI|y$K zm+G8S{#6^kK`O>WzL-+p07&o*SEcG1##tm@Uy8=}CoNC1rhHZmUmo@h=Y-8?b5?0r z+Vn?7Hul0U>?-YYdWbH(!8`W`ZKaY&V)z(IW&3W3@z$br=7?(Mjsp zn{>&UqiH;9imNAL>6N-XM5NCjA$02YEz@(?LZ(S*TJH22xmV<_#tkp~_nZ~Q=OU5v5z1!_bYJI=-3{0cB` zFkR$FH2fArZ~Q=O@ebY?YDAfQz6i9N-#qX$`OG21@N0zL&94dkPBh2nkm2T6>53nw zA7Or1b>g=X`~omD%7nER25x@!uK2~n&cyE~=;?>$N5#8@e0#_!YWdtQXw07l19!d{ zFC68-qTaaUKsV43K;y3S96JDayH4Pa?op&+m4`$nz>sGV9K4tg<~!4haKmZ=3Smf? ziiGPjQd$!q5??&Wc89?57+~I@gC_`T}8rk_rAC_ z!8;SZgs-BxG%CVE7Ni>=i=P2J@IV3 zo8?!RY-3Khc<~l9aJ#3uSK!{q?K}r}U%HK4S%Ac7yC?ZTDCzW`LG)@}BNr0XWIY>A zTK5=wvfd&|Ivr@gk3VxAfjRGu@iqa#4>RsS)Guf~m?oHUM_or8qA72bM*Z)LVn63e z(o~!k?Og#nhKoaoJ-Hp@V2*0-D za#m6d83R=P@kr%VH2+KmrM|4DzG->K_q3`@A^{fS+o-r*`_*^UBP^EUv#1p&(rVI< zEW}%YR?*~$BPp(0%AA2sr<7j%7@3}xo)t*OLI9V(%Z3M%CqvZC97xS6DQj3BfUubWMPFPpGA(1c zHIds$>cgv+%|0Ot)@Kw>$)9dTdvH>n-nhJ`sdzCE@)i_U8r?Us&Zy(dgyfx(o~3U~ zL;MLPd$?{+{|8@(!i^nt7I@eRQDxzAS6;6&)pt+bbI%0ySDZVXiiiH`&WC5H&}LS} zIf>x`ZCF17db&66c0=!O!^Tet=Hzb_-Ej_Jl(Cxx1EZ&10|>Tmql~um8e|^QaTJc2 z4%HC@8L!T3kVnwMzYlYx48P;nAee~LKlu?&zHn_pKbd%_xc{7_wd$YlXn%>I?oc3F z9q!oY8D;XN1_ti(n1^r!Fk`qFK*QmF3wje*Xw`Q{#!(~6@LLYr&2O2DABQHoV|uQ` z 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

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�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

81Wqb<71CW_;{Ky2MBiI80naO763&>1l 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 E0gsN{S^xk5 diff --git a/ports/cortex_a8/gnu/example_build/libgcc.a b/ports/cortex_a8/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 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 z 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 dhn|!HpOEmEh$!@SiRZoRqCc?z|Ecwv{|7^Je? tx_thread_new_time_slice, new_time_slice); TX_EL_END_FILTER +#define TX_EL_THREAD_TERMINATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_TERMINATE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_SLEEP_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SLEEP, timer_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_SUSPEND_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SUSPEND, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_RELINQUISH_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_RELINQUISH); TX_EL_END_FILTER +#define TX_EL_THREAD_RESUME_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESUME, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PRIORITY_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PRIORITY_CHANGE, thread_ptr, thread_ptr -> tx_thread_priority, new_priority); TX_EL_END_FILTER +#define TX_EL_THREAD_PREEMPTION_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PREEMPTION_CHANGE, thread_ptr, thread_ptr -> tx_thread_preempt_threshold, new_threshold); TX_EL_END_FILTER +#define TX_EL_THREAD_WAIT_ABORT_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_WAIT_ABORT, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_THREAD_ENTRY_EXIT_NOTIFY, thread_ptr, thread_entry_exit_notify); TX_EL_END_FILTER +#define TX_EL_THREAD_RESET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_PERFORMANCE_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_STACK_ERROR_NOTIFY, stack_error_handler); TX_EL_END_FILTER +#define TX_EL_TIME_SET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_SET, new_time); TX_EL_END_FILTER +#define TX_EL_TIME_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_GET, _tx_timer_system_clock); TX_EL_END_FILTER +#define TX_EL_TIMER_DELETE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DELETE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_CREATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate); TX_EL_END_FILTER +#define TX_EL_TIMER_CHANGE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_TIMER_CHANGE, timer_ptr, initial_ticks, reschedule_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_IDENTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_IDENTIFY); TX_EL_END_FILTER +#define TX_EL_TIMER_DEACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DEACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_ACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_ACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_PERFORMANCE_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_GET, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_DELETE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_DELETE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CREATE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_CREATE, semaphore_ptr, initial_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PRIORITIZE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PRIORITIZE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CEILING_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_SEMAPHORE_CEILING_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, ceiling); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT_NOTIFY, semaphore_ptr, semaphore_put_notify); TX_EL_END_FILTER +#define TX_EL_QUEUE_FRONT_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_FRONT_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_RECEIVE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_RECEIVE, queue_ptr, destination_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_FLUSH_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_FLUSH, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_DELETE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_DELETE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_CREATE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_QUEUE_CREATE, queue_ptr, queue_start, queue_size, message_size); TX_EL_END_FILTER +#define TX_EL_QUEUE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PRIORITIZE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PRIORITIZE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PERFORMANCE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_NOTIFY_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND_NOTIFY, queue_ptr, queue_send_notify); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_GET, group_ptr, requested_flags, get_option); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_DELETE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_DELETE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_CREATE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_CREATE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_EVENT_FLAGS_SET_NOTIFY, group_ptr, events_set_notify); TX_EL_END_FILTER +#define TX_EL_BYTE_RELEASE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BYTE_RELEASE, pool_ptr, memory_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_DELETE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_CREATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_POOL_CREATE, pool_ptr, pool_start, pool_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PRIORITIZE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_ALLOCATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_ALLOCATE, pool_ptr, memory_ptr, memory_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_BLOCK_RELEASE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_RELEASE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_DELETE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_CREATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_BLOCK_POOL_CREATE, pool_ptr, pool_start, pool_size, block_size); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PRIORITIZE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_ALLOCATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_ALLOCATE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_MUTEX_CREATE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_MUTEX_CREATE, mutex_ptr, inherit); TX_EL_END_FILTER +#define TX_EL_MUTEX_DELETE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_DELETE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_GET, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PRIORITIZE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PRIORITIZE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PUT_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_PUT, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PERFORMANCE_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER + + +#endif + + +/* Define Event Log function prototypes. */ + +VOID _tx_el_initialize(VOID); +UINT _tx_el_thread_register(TX_THREAD *thread_ptr); +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr); +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4); +VOID _tx_el_thread_running(TX_THREAD *thread_ptr); +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr); +VOID _tx_el_interrupt(UINT interrupt_number); +VOID _tx_el_interrupt_end(UINT interrupt_number); +VOID _tx_el_interrupt_control_call(void); +VOID _tx_el_event_log_on(void); +VOID _tx_el_event_log_off(void); +VOID _tx_el_event_filter_set(UINT filter); + + +/* Define macros that are used inside the ThreadX source code. + If event logging is disabled, these macros will be defined + as white space. */ + +#ifdef TX_ENABLE_EVENT_LOGGING +#ifndef TX_NO_EVENT_INFO +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =\ + (ULONG) e;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#endif +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) +#define TX_EL_THREAD_REGISTER(a) +#define TX_EL_THREAD_UNREGISTER(a) +#define TX_EL_INITIALIZE +#endif + +#endif + diff --git a/ports/cortex_a9/ghs/inc/tx_ghs.h b/ports/cortex_a9/ghs/inc/tx_ghs.h new file mode 100644 index 00000000..ca976916 --- /dev/null +++ b/ports/cortex_a9/ghs/inc/tx_ghs.h @@ -0,0 +1,77 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#ifndef _TX_GHS_H_ +#define _TX_GHS_H_ + +#include +#include +#include +#include + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +extern void *__ghs_GetThreadLocalStorageItem(int specifier); + +/* Thread-local storage routines for Green Hills releases 5.x and beyond. + The following specifiers are used when calling + __ghs_GetThreadLocalStorageItem. + + If __ghs_GetThreadLocalStorageItem is customized to + return a per-thread errno value, define the preprocessor symbol + USE_THREAD_LOCAL_ERRNO in ind_errn.c. + */ + +enum __ghs_ThreadLocalStorage_specifier { + __ghs_TLS_asctime_buff, + __ghs_TLS_tmpnam_space, + __ghs_TLS_strtok_saved_pos, + __ghs_TLS_Errno, + __ghs_TLS_gmtime_temp, + __ghs_TLS___eh_globals, + __ghs_TLS_SignalHandlers +}; +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ +typedef void (*SignalHandler)(int); + +typedef struct +{ + int Errno; /* errno. */ + SignalHandler SignalHandlers[_SIGMAX]; /* signal() buffer. */ + char tmpnam_space[L_tmpnam]; /* tmpnam(NULL) buffer. */ + char asctime_buff[30]; /* . */ + char *strtok_saved_pos; /* strtok() position. */ + struct tm gmtime_temp; /* gmtime() and localtime() buffer. */ + void *__eh_globals; /* Pointer for C++ exception handling. */ +} ThreadLocalStorage; + +ThreadLocalStorage *GetThreadLocalStorage(void); +#endif + + +void __ghsLock(void); +void __ghsUnlock(void); + +int __ghs_SaveSignalContext(jmp_buf); +void __ghs_RestoreSignalContext(jmp_buf); + +/* prototypes for FILE lock routines. */ +void __ghs_flock_file(void *); +void __ghs_funlock_file(void *); +int __ghs_ftrylock_file(void *); +void __ghs_flock_create(void **); +void __ghs_flock_destroy(void *); + +/* prototype for GHS/ThreadX error shell checking. */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal); + +#endif /* _TX_GHS_H_ */ diff --git a/ports/cortex_a9/green/inc/tx_port.h b/ports/cortex_a9/ghs/inc/tx_port.h similarity index 91% rename from ports/cortex_a9/green/inc/tx_port.h rename to ports/cortex_a9/ghs/inc/tx_port.h index 40887c61..d6238ba5 100644 --- a/ports/cortex_a9/green/inc/tx_port.h +++ b/ports/cortex_a9/ghs/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-A9/Green Hills */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-A9/GHS */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -78,7 +78,7 @@ #include "tx_ghs.h" -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -114,12 +114,12 @@ typedef unsigned short USHORT; #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #ifdef TX_ENABLE_FIQ_SUPPORT #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ @@ -134,13 +134,13 @@ typedef unsigned short USHORT; /* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ -#define TX_EL_TICKS_PER_SECOND 1000000 +#define TX_EL_TICKS_PER_SECOND 1000000 /* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply - simulate the time-stamp source with a counter. */ + simulate the time-stamp source with a counter. */ -#define read_tbu() _tx_el_time_base_upper -#define read_tbl() ++_tx_el_time_base_lower +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower /* Define the port specific options for the _tx_build_options variable. This variable indicates @@ -174,7 +174,7 @@ typedef unsigned short USHORT; #define TX_INLINE_INITIALIZATION -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -186,16 +186,16 @@ typedef unsigned short USHORT; /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 #define TX_THREAD_EXTENSION_2 ULONG tx_thread_vfp_enable; \ VOID * tx_thread_eh_globals; \ int Errno; /* errno. */ \ char * strtok_saved_pos; /* strtok() position. */ -#define TX_THREAD_EXTENSION_3 +#define TX_THREAD_EXTENSION_3 /* Define the port extensions of the remaining ThreadX objects. */ @@ -209,11 +209,11 @@ typedef unsigned short USHORT; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -243,7 +243,7 @@ typedef unsigned short USHORT; extern void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr); \ __tx_cpp_exception_cleanup(thread_ptr); \ } -#else +#else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) \ { \ #pragma weak __cpp_exception_cleanup \ @@ -281,18 +281,18 @@ typedef unsigned short USHORT; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) m = m & ((ULONG) (-((LONG) m))); \ b = __CLZ32(m); \ - b = 31 - b; + b = 31 - b; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -302,7 +302,7 @@ typedef unsigned short USHORT; unsigned int _tx_thread_interrupt_disable(void); void _tx_thread_interrupt_restore(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); @@ -310,7 +310,7 @@ void _tx_thread_interrupt_restore(unsigned int new_po #else -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER >= 350) @@ -349,7 +349,7 @@ asm int disable_ints(void) MSR CPSR_c,r1 #else #ifdef TX_ENABLE_FIQ_SUPPORT - CPSID if + CPSID if #else CPSID i #endif @@ -395,7 +395,7 @@ void tx_thread_vfp_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A9/Green Hills Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A9/Green Hills Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports/cortex_a9/green/readme_threadx.txt b/ports/cortex_a9/ghs/readme_threadx.txt similarity index 100% rename from ports/cortex_a9/green/readme_threadx.txt rename to ports/cortex_a9/ghs/readme_threadx.txt diff --git a/ports/cortex_a9/ghs/src/tx_el.c b/ports/cortex_a9/ghs/src/tx_el.c new file mode 100644 index 00000000..d8f056d7 --- /dev/null +++ b/ports/cortex_a9/ghs/src/tx_el.c @@ -0,0 +1,1165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_EL_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_el.h" +#include "string.h" + + +/* Define global variables used to manage the event pool. */ + +UCHAR *_tx_el_tni_start; +UCHAR **_tx_el_current_event; +UCHAR *_tx_el_event_area_start; +UCHAR *_tx_el_event_area_end; +UINT _tx_el_maximum_events; +ULONG _tx_el_total_events; +UINT _tx_el_event_filter; +ULONG _tx_el_time_base_upper; +ULONG _tx_el_time_base_lower; + +extern char __ghsbegin_eventlog[]; +extern char __ghsend_eventlog[]; + +extern TX_THREAD *_tx_thread_current_ptr; +UINT _tx_thread_interrupt_control(UINT new_posture); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the Event Log (in the format dictated by the */ +/* GHS Event Analyzer) and sets up various information for subsequent */ +/* operation. The start and end of the Event Log is determined by the */ +/* .eventlog section in the linker control file. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_initialize(VOID) +{ + +UCHAR *work_ptr; +UCHAR *read_ptr; +ULONG event_log_size; +UCHAR *end_ptr; +UINT i; + + + /* Clear total event counter. */ + _tx_el_total_events = 0; + + /* Clear event filter. */ + _tx_el_event_filter = 0; + + /* First, pickup the starting and ending address of the Event Log memory. */ + work_ptr = (unsigned char *) __ghsbegin_eventlog; + end_ptr = (unsigned char *) __ghsend_eventlog; + + /* Calculate the event log size. */ + event_log_size = end_ptr - work_ptr; + + /* Subtract off the number of bytes in the header and the TNI area. */ + event_log_size = event_log_size - (TX_EL_HEADER_SIZE + + (TX_EL_TNI_ENTRY_SIZE * TX_EL_TNIS)); + + /* Make sure the event log is evenly divisible by the event size. */ + event_log_size = (event_log_size/TX_EL_EVENT_SIZE) * TX_EL_EVENT_SIZE; + + /* Build the Event Log header. */ + + /* Setup the Event Log Version ID. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_VERSION_ID; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the TNIS (number of thread names) field. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_TNIS; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the EVPS (event pool size) field. */ + *((ULONG *) work_ptr) = event_log_size; + work_ptr = work_ptr + sizeof(ULONG); + + /* Remember the maximum number of events. */ + _tx_el_maximum_events = event_log_size/TX_EL_EVENT_SIZE; + + /* Setup max_events field. */ + *((ULONG *) work_ptr) = _tx_el_maximum_events; + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup the evploc (location of event pool). */ + *((ULONG *) work_ptr) = (ULONG) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Save the current event pointer. */ + _tx_el_current_event = (UCHAR **) work_ptr; + + /* Setup event_ptr (pointer to oldest event) field to the start + of the event pool. */ + *_tx_el_current_event = (UCHAR *) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup tbfreq (the number of ticks in a second) field. */ + *((ULONG *) work_ptr) = TX_EL_TICKS_PER_SECOND; + work_ptr = work_ptr + sizeof(ULONG); + + /* At this point we are pointing at the Thread Name Information (TNI) array. */ + + /* Remember the start of this for future updates. */ + _tx_el_tni_start = work_ptr; + + /* Clear the entire TNI array, this is the initial setting. */ + end_ptr = work_ptr + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE); + memset((void *)work_ptr, 0, (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = end_ptr; + + /* At this point, we are pointing at the actual Event Entry area. */ + + /* Remember the start of the actual event log area. */ + _tx_el_event_area_start = work_ptr; + + /* Clear the entire Event area. */ + end_ptr = work_ptr + event_log_size; + memset((void *)work_ptr, 0, event_log_size); + work_ptr = end_ptr; + + /* Save the end pointer for later use. */ + _tx_el_event_area_end = work_ptr; + + /* Setup an entry to resolve all activities from initialization and from + an idle system. */ + work_ptr = _tx_el_tni_start; + read_ptr = (UCHAR *) "Initialization/System Idle"; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID to NULL. */ + *((ULONG *) (_tx_el_tni_start + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) TX_NULL; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (_tx_el_tni_start + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Clear the time base global variables. */ + _tx_el_time_base_upper = 0; + _tx_el_time_base_lower = 0; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_register(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT i; + + + /* First of all, search for a free slot in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is available. */ + if (*(entry_ptr + TX_EL_TNI_VALID_OFFSET) == TX_EL_INVALID_ENTRY) + break; + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Check to see if there were no more valid entries. */ + if (i >= TX_EL_TNIS) + return(TX_EL_NO_MORE_TNI_ROOM); + + /* Otherwise, we have room in the TNI and a valid record. */ + + /* Setup the thread's name. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) thread_ptr; + + /* Setup the thread priority. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_PRIORITY_OFF)) = (ULONG) thread_ptr -> tx_thread_priority; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (entry_ptr + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Thread name has been registered. */ + return(TX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT found; +UINT i, j; + + + /* First of all, search for a match in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is a match. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + found = TX_TRUE; + j = 0; + do + { + + /* Determine if this character is the same. */ + if (*work_ptr != *read_ptr) + { + + /* Set found to false and fall out of the loop. */ + found = TX_FALSE; + break; + } + else if (*work_ptr == 0) + { + + /* Null terminated, just break the loop. */ + break; + } + else + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + } + + /* Increment the character count. */ + j++; + + } while(j < TX_EL_TNIS); + + + /* Was a match found? */ + if (found) + { + + /* Yes, mark the entry as available now. */ + *(entry_ptr + TX_EL_TNI_VALID_OFFSET) = TX_EL_INVALID_ENTRY; + + /* Get out of the loop! */ + break; + } + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Determine status to return. */ + if (found) + return(TX_SUCCESS); + else + return(TX_EL_NAME_NOT_FOUND); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_user_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user event into the event log. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* sub_type Event subtype for kernel call */ +/* info_1 First information field */ +/* info_2 Second information field */ +/* info_3 Third information field */ +/* info_4 Fourth information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT upper_tb; +UCHAR *entry_ptr; + + /* Disable interrupts. */ + TX_DISABLE + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_USER_EVENT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) sub_type; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) info_1; + + /* Store the second info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) = + (ULONG) info_2; + + /* Store the third info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) = + (ULONG) info_3; + + /* Store the fourth info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) = + (ULONG) info_4; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + /* Restore interrupts. */ + TX_RESTORE +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_running PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread change event into the event */ +/* log, which indicates that a context switch is taking place. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_schedule ThreadX scheduler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_running(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) 0; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) thread_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_preempted PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread preempted event into the event */ +/* log, which indicates that an interrupt occurred that made a higher */ +/* priority thread ready for execution. In this case, the previously */ +/* executing thread has an event entered to indicate it is no longer */ +/* running. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_context_restore ThreadX context restore */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_STATUS_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_READY; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt event into the log, which */ +/* indicates the start of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_INTERRUPT_SUB_TYPE; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt end event into the log, which */ +/* indicates the end of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt_end(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_END_OF_INTERRUPT; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_control PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function remaps the tx_interrupt_control service call so that */ +/* it can be tracked in the event log. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt posture */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_interrupt_control Interrupt control service */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_interrupt_control(UINT new_posture) +{ + +TX_INTERRUPT_SAVE_AREA +UINT old_posture; + + + TX_EL_NO_INTERRUPT_EVENTS + + TX_DISABLE + TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_INTERRUPT_CONTROL, _tx_thread_current_ptr, new_posture) + TX_RESTORE + + TX_EL_END_FILTER + + old_posture = _tx_thread_interrupt_control(new_posture); + return(old_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_on PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables all event filters. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_on(void) +{ + + /* Disable all event filters. */ + _tx_el_event_filter = TX_EL_ENABLE_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_off PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets all event filters, thereby turning event */ +/* logging off. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_off(void) +{ + + /* Set all event filters. */ + _tx_el_event_filter = TX_EL_FILTER_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the events filters specified by the user. */ +/* */ +/* INPUT */ +/* */ +/* filter Events to filter */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_filter_set(UINT filter) +{ + + /* Apply the user event filter. */ + _tx_el_event_filter = filter; +} + diff --git a/ports/cortex_a9/ghs/src/tx_ghs.c b/ports/cortex_a9/ghs/src/tx_ghs.c new file mode 100644 index 00000000..30b8054e --- /dev/null +++ b/ports/cortex_a9/ghs/src/tx_ghs.c @@ -0,0 +1,485 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" +#include +#include + +/* Allow these routines to access the following ThreadX global variables. */ +extern ULONG _tx_thread_created_count; +extern TX_THREAD *_tx_thread_created_ptr; +extern TX_THREAD *_tx_thread_current_ptr; + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +/* Thread-local storage routines for Green Hills releases 5.x and above. */ +/* + Thread-Local (Per-Thread) Library Data Retrieval + ================================================ + + __ghs_ThreadLocalStorage_specifier defines all library data items + that the Green Hills libraries allow to be allocated per-thread. + + An implementation can choose which of these data items to allocate + for each thread. For example, an implementation may choose to + allocate an errno value for each thread, but not the strtok_saved_pos + pointer. The application could then use strtok_r instead of strtok for + correct operation. + + To add per-thread library data, define one of the + TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item + or items in each thread control block TX_THREAD. + + If C++ with exceptions is being used, the __eh_globals entry must be + allocated for each thread. This is typically done by default using + TX_THREAD_EXTENSION_1 in tx_port.h. + + If __ghs_GetThreadLocalStorageItem is customized to return a + per-thread errno value, you should also: + + * Customize the System Library for your project + * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in + src/libsys/ind_errn.c + + If you customize the System Library, you should remove ind_thrd.c + from the libsys.gpj subproject. + + */ + +/* Provide global __eh_globals value to support C++ exception handling + outside a thread context. This name also forces this module to be + included in the linked program instead of the ind_thrd.o module from + the System Library libsys.a. + */ +static void *__eh_globals; + +#pragma ghs startnomisra +void *__ghs_GetThreadLocalStorageItem(int specifier) +{ + void *ptlsitem = (void *)0; + switch (specifier) { + case (int)__ghs_TLS_Errno: + /* Set ptslsitem to the address of the per-thread errno value. + The per-thread errno value should have the type int. + + If returning a per-thread errno value, follow the steps + above. + + This item is used by numerous library functions. + */ + break; + case (int)__ghs_TLS_SignalHandlers: + /* Set ptslsitem to the address of the per-thread SignalHandlers + array. The per-thread SignalHandlers array should have the + array type as in the following declaration: + SignalHandler SignalHandlers[_SIGMAX]; + The SignalHandler type and _SIGMAX constant are defined in + ind_thrd.h. + + This item is used by the library functions signal() and + raise(). + */ + break; + case (int)__ghs_TLS_asctime_buff: + /* Set ptslsitem to the address of the per-thread asctime_buff + array. The per-thread asctime_buff array should have the + array type as in the following declaration: + char asctime_buff[30]; + + This item is used by the library functions asctime() and + ctime(). The library provides asctime_r() and ctime_r(), + inherently thread-safe versions of these functions. + */ + break; + case (int)__ghs_TLS_tmpnam_space: + /* Set ptslsitem to the address of the per-thread tmpnam_space + array. The per-thread tmpnam_space array should have the + array type as in the following declaration: + char tmpnam_space[L_tmpnam]; + The constant is defined in + + This item is used by the library function tmpnam() when + passed NULL. The library provides tmpnam_r(), an + inherently thread-safe version of tmpnam(). + */ + break; + case (int)__ghs_TLS_strtok_saved_pos: + /* Set ptslsitem to the address of the per-thread + strtok_saved_pos pointer. The per-thread strtok_saved_pos + pointer should have the type "char *". + + This item is used by the library function strtok(). + The library provides strtok_r(), an inherently thread-safe + version of strtok(). + */ + break; + case (int)__ghs_TLS_gmtime_temp: + /* Set ptslsitem to the address of the per-thread gmtime_temp + value. The per-thread gmtime_temp value should have the + type "struct tm" defined in time.h, included by indos.h. + + This item is used by the library functions gmtime() and + localtime(). The library provides gmtime_r() and + localtime_r(), inherently thread-safe versions of these + functions. + */ + break; + case (int)__ghs_TLS___eh_globals: + /* Set ptslsitem to the address of the per-thread __eh_globals + value. The per-thread __eh_globals value should have the + type "void *". + + This item is used by C++ exception handling. + */ + if (_tx_thread_current_ptr) + ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + ptlsitem = (void *)&__eh_globals; + break; + } + return ptlsitem; +} +#pragma ghs endnomisra +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ + +/* + * ThreadX C and C++ thread-safe library support routines. + * + * This implementation merely tries to guarantee thread safety within + * individual C library calls such as malloc() and free(), but it does + * not attempt to solve the problems associated with the following + * multithreaded issues: + * + * 1. Use of errno. This can be made thread-safe by adding errno + * to TX_THREAD_PORT_EXTENSION and using that within a modified + * version of libsys/ind_errno.c. + * + * 2. Thread safety ACROSS library calls. Certain C library calls either + * return pointers to statically-allocated data structures or maintain + * state across calls. These include strtok(), asctime(), gmtime(), + * tmpnam(NULL), signal(). To make such C library routines thread-safe + * would require adding a ThreadLocalStorage struct to the thread control + * block TX_THREAD. Since relatively few applications make use of these + * library routines, the implementation provided here uses a single, global + * ThreadLocalStorage data structure rather than greatly increasing the size + * of the thread control block TX_THREAD. + * + * The ThreadX global variable _tx_thread_current_ptr points to the + * current thread's control block TX_THREAD. If a ThreadLocalStorage struct + * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage + * should be modified to return &(_tx_thread_current_ptr->tx_tls). + */ + +static ThreadLocalStorage GlobalTLS; + +ThreadLocalStorage *GetThreadLocalStorage() +{ + return &GlobalTLS; +} +#endif + +/* + * Use a global ThreadX mutex to implement thread safety within C and C++ + * library routines. + * + */ +TX_MUTEX __ghLockMutex; + +/* + * Acquire general lock. Blocks until the lock becomes available. + * Use tx_mutex_get to implement __ghsLock + */ +void __ghsLock(void) +{ + tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER); +} + +/* + * Release general lock + * Use tx_mutex_put to implement __ghsUnlock + */ +void __ghsUnlock(void) +{ + tx_mutex_put(&__ghLockMutex); +} + +/* ThreadX Initialization function prototype. */ +void _tx_initialize_kernel_setup(void); + +void __gh_lock_init(void) +{ + /* Initialize the low-level portions of ThreadX. */ + _tx_initialize_kernel_setup(); + + /* Create the global thread lock mutex. */ + tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT); +} + +/* + Saving State Across setjmp() Calls + ================================== + + These routines can be used to save and restore arbitrary state + across calls to setjmp() and longjmp(). +*/ +int __ghs_SaveSignalContext(jmp_buf jmpbuf) +{ + return 0; +} + +/* Restore arbitrary state across a longjmp() */ +void __ghs_RestoreSignalContext(jmp_buf jmpbuf) +{ +} + +#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560) +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +#else + if (_tx_thread_current_ptr) + + /* Return thread-specific __eh_globals pointer. */ + return _tx_thread_current_ptr->tx_thread_eh_globals; + else + /* Return the global __eh_globals pointer. */ + return GlobalTLS.__eh_globals; +#endif +} +#endif + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +#pragma weak __cpp_exception_init +extern void __cpp_exception_init(void **); +#pragma weak __cpp_exception_cleanup +extern void __cpp_exception_cleanup(void **); + +/* __tx_cpp_exception_init retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_init. + */ +void __tx_cpp_exception_init(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_init) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_init(peh_globals); + } +} + +/* __tx_cpp_exception_cleanup retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_cleanup. + */ +void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_cleanup) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_cleanup(peh_globals); + } +} + +/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize + exceptions for the global context. + */ +void __ghs_cpp_exception_init() { + __tx_cpp_exception_init((void *)0); +} + +/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up + exceptions for the global context. + */ +void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + __tx_cpp_exception_cleanup((void *)0); +} +#endif + + +/* + File Locks + ====================== + + These routines can be customized to implement per-file locks to allow + thread-safe I/O. + +*/ + +/* Acquire lock for FILE *addr */ +void __ghs_flock_file(void *addr) +{ + tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER); +} + +/* Release lock for FILE *addr */ +void __ghs_funlock_file(void *addr) +{ + tx_mutex_put((TX_MUTEX *)addr); +} + +/* Non blocking acquire lock for FILE *addr. May return -1 if */ +/* not implemented. Returns 0 on success and nonzero otherwise. */ +int __ghs_ftrylock_file(void *addr) +{ + return -1; +} + +/* Calls to initialize local lock data structures before they */ +/* are used. */ +void __ghs_flock_create(void **addr) +{ + *addr = (void *)(&__ghLockMutex); +} +void __ghs_flock_destroy(void *addr) {} + + +/* + * ThreadX Peak Stack Checking support routines. + * + * All of these routines are called by MULTI's ThreadX-aware debugging + * package to determine the peak stack use for one thread or for all threads. + * + * These routines are included in this file in order to guarantee that they will + * be available while debugging with MULTI. These routines are not referenced by + * any other part of the ThreadX system. + * + * _txs_thread_stack_check: return the peak stack usage for a thread. + * + * _txs_thread_stack_check_2: store the peak stack usage for all threads + * in the tx_thread_stack_size field of each thread + * control block, TX_THREAD. This routine takes + * advantage of the redundancy within the TX_THREAD + * structure since tx_thread_stack_size can be computed + * from the tx_thread_stack_start and tx_thread_stack_end + * fields of TX_THREAD. + * + * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2 + * call by computing the stack size for each + * thread and storing the result in the + * tx_thread_stack_size field of each thread control + * block TX_THREAD. + * + * These three routines do not support architectures such as i960 or StarCore + * where the stack grows up instead of down. + * + */ +#ifndef TX_DISABLE_STACK_CHECKING + +ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr) +{ + CHAR *cp; /* Pointer inside thread's stack. */ + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start; + cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Return the number of bytes from cp up to and including the + end of the stack. */ + return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1); + } + } + return thread_ptr->tx_thread_stack_size; +} + + +int _txs_thread_stack_check_2(void) { + CHAR * cp; /* Pointer inside thread's stack. */ + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end; + ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Store the number of bytes from cp up to and including the + end of the stack in the tx_thread_stack_size field. */ + tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1; + break; + } + + } + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +int _txs_thread_stack_check_2_fixup(void) { + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and + tx_thread_stack_start fields. */ + tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1; + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +#endif /* TX_DISABLE_STACK_CHECKING */ diff --git a/ports/cortex_a9/ghs/src/tx_ghse.c b/ports/cortex_a9/ghs/src/tx_ghse.c new file mode 100644 index 00000000..6369df77 --- /dev/null +++ b/ports/cortex_a9/ghs/src/tx_ghse.c @@ -0,0 +1,49 @@ +/* + * ThreadX C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 560) +#ifdef _WIN32 +/* Windows uses a different linker, so include a stub routine, never called, + to pull in __cpp_exception_init and __cpp_exception_cleanup */ +extern void __cpp_exception_init(void **); +extern void __cpp_exception_cleanup(void **); +void __tx_win32_pull_in_exceptions(void) { + __cpp_exception_init(0); + __cpp_exception_cleanup(0); +} +#else +#pragma ghs reference __cpp_exception_init +#pragma ghs reference __cpp_exception_cleanup +#endif + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +} +#endif diff --git a/ports/cortex_a9/green/src/tx_thread_context_restore.arm b/ports/cortex_a9/ghs/src/tx_thread_context_restore.arm similarity index 94% rename from ports/cortex_a9/green/src/tx_thread_context_restore.arm rename to ports/cortex_a9/ghs/src/tx_thread_context_restore.arm index ec6e2f09..ff929c7f 100644 --- a/ports/cortex_a9/green/src/tx_thread_context_restore.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,47 +41,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_context_restore(VOID) @@ -117,13 +114,13 @@ _tx_thread_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -135,7 +132,7 @@ _tx_thread_context_restore: __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -225,7 +222,7 @@ _tx_skip_irq_vfp_save: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - + STR r2, [r0, 24] # Save thread's time-slice MOV r2, 0 # Clear value STR r2, [r3] # Disable global time-slice flag diff --git a/ports/cortex_a9/green/src/tx_thread_context_save.arm b/ports/cortex_a9/ghs/src/tx_thread_context_save.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_context_save.arm rename to ports/cortex_a9/ghs/src/tx_thread_context_save.arm index cd45ced9..acfaeb49 100644 --- a/ports/cortex_a9/green/src/tx_thread_context_save.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,46 +39,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_context_save(VOID) @@ -93,7 +90,7 @@ _tx_thread_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers #ifdef TX_ENABLE_FIQ_SUPPORT #ifdef TX_BEFORE_ARMV6 @@ -119,7 +116,7 @@ _tx_thread_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -135,7 +132,7 @@ _tx_thread_context_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing __tx_thread_not_nested_save: /* } */ @@ -149,13 +146,13 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, r10, r12, lr} # Store other registers /* Save the current stack pointer in the thread's control block. */ @@ -175,7 +172,7 @@ __tx_thread_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing /* } else @@ -185,7 +182,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit @@ -200,7 +197,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, 16 # Recover saved registers - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing .type _tx_thread_context_save,$function .size _tx_thread_context_save,.-_tx_thread_context_save diff --git a/ports/cortex_a9/green/src/tx_thread_fiq_context_restore.arm b/ports/cortex_a9/ghs/src/tx_thread_fiq_context_restore.arm similarity index 94% rename from ports/cortex_a9/green/src/tx_thread_fiq_context_restore.arm rename to ports/cortex_a9/ghs/src/tx_thread_fiq_context_restore.arm index a8e8b849..98ca7b4a 100644 --- a/ports/cortex_a9/green/src/tx_thread_fiq_context_restore.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_fiq_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -42,47 +42,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_restore Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_restore Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the fiq interrupt context when processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* FIQ ISR Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the fiq interrupt context when processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* FIQ ISR Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_fiq_context_restore(VOID) @@ -114,13 +111,13 @@ _tx_thread_fiq_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_fiq_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -132,7 +129,7 @@ _tx_thread_fiq_context_restore: __tx_thread_fiq_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -191,7 +188,7 @@ __tx_thread_fiq_preempt_restore: MOV r5, SVC_MODE # Build SVC mode CPSR MSR CPSR_c, r5 # Enter SVC mode STMDB sp!, {r0-r3} # Save r0-r3 on thread's stack - + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer diff --git a/ports/cortex_a9/green/src/tx_thread_fiq_context_save.arm b/ports/cortex_a9/ghs/src/tx_thread_fiq_context_save.arm similarity index 91% rename from ports/cortex_a9/green/src/tx_thread_fiq_context_save.arm rename to ports/cortex_a9/ghs/src/tx_thread_fiq_context_save.arm index f98a5f6b..bef0ff6c 100644 --- a/ports/cortex_a9/green/src/tx_thread_fiq_context_save.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_fiq_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -33,46 +33,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_save Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_save Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_fiq_context_save(VOID) @@ -87,7 +84,7 @@ _tx_thread_fiq_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state CMP r2, 0 # Is this the first interrupt? @@ -102,7 +99,7 @@ _tx_thread_fiq_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -118,7 +115,7 @@ _tx_thread_fiq_context_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing __tx_thread_fiq_not_nested_save: /* } */ @@ -132,16 +129,16 @@ __tx_thread_fiq_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, lr} # Store other registers, Note that we don't - /* # need to save sl and ip since FIQ has - # copies of these registers. Nested + /* # need to save sl and ip since FIQ has + # copies of these registers. Nested # interrupt processing does need to save # these registers. */ @@ -162,7 +159,7 @@ __tx_thread_fiq_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing /* } else @@ -182,15 +179,15 @@ __tx_thread_fiq_idle_system_save: #endif /* Not much to do here, save the current SPSR and LR for possible - use in IRQ interrupted in idle system conditions, and return to + use in IRQ interrupted in idle system conditions, and return to FIQ interrupt processing. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, lr} # Store other registers that will get used - /* # or stripped off the stack in context + /* # or stripped off the stack in context # restore */ - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing .type _tx_thread_fiq_context_save,$function .size _tx_thread_fiq_context_save,.-_tx_thread_fiq_context_save diff --git a/ports/cortex_a9/green/src/tx_thread_fiq_nesting_end.arm b/ports/cortex_a9/ghs/src/tx_thread_fiq_nesting_end.arm similarity index 91% rename from ports/cortex_a9/green/src/tx_thread_fiq_nesting_end.arm rename to ports/cortex_a9/ghs/src/tx_thread_fiq_nesting_end.arm index 5c5de37f..d99bac98 100644 --- a/ports/cortex_a9/green/src/tx_thread_fiq_nesting_end.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_fiq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,48 +41,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_end Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_end Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ -/* processing from system mode back to FIQ mode prior to the ISR */ -/* calling _tx_thread_fiq_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ +/* processing from system mode back to FIQ mode prior to the ISR */ +/* calling _tx_thread_fiq_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_fiq_nesting_start.arm b/ports/cortex_a9/ghs/src/tx_thread_fiq_nesting_start.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_fiq_nesting_start.arm rename to ports/cortex_a9/ghs/src/tx_thread_fiq_nesting_start.arm index a2f57b23..ffc2ac08 100644 --- a/ports/cortex_a9/green/src/tx_thread_fiq_nesting_start.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_fiq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,45 +39,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_start Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_start Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_context_save has been called and switches the FIQ */ -/* processing to the system mode so nested FIQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_context_save has been called and switches the FIQ */ +/* processing to the system mode so nested FIQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_interrupt_control.arm b/ports/cortex_a9/ghs/src/tx_thread_interrupt_control.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_interrupt_control.arm rename to ports/cortex_a9/ghs/src/tx_thread_interrupt_control.arm index c1533232..e067d707 100644 --- a/ports/cortex_a9/green/src/tx_thread_interrupt_control.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_interrupt_control.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,39 +38,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_interrupt_disable.arm b/ports/cortex_a9/ghs/src/tx_thread_interrupt_disable.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_interrupt_disable.arm rename to ports/cortex_a9/ghs/src/tx_thread_interrupt_disable.arm index 9ee497a9..af5e60a8 100644 --- a/ports/cortex_a9/green/src/tx_thread_interrupt_disable.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_interrupt_disable.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,38 +38,38 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_interrupt_restore.arm b/ports/cortex_a9/ghs/src/tx_thread_interrupt_restore.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_interrupt_restore.arm rename to ports/cortex_a9/ghs/src/tx_thread_interrupt_restore.arm index a8db80f8..86e92c98 100644 --- a/ports/cortex_a9/green/src/tx_thread_interrupt_restore.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_interrupt_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -31,39 +31,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_irq_nesting_end.arm b/ports/cortex_a9/ghs/src/tx_thread_irq_nesting_end.arm similarity index 91% rename from ports/cortex_a9/green/src/tx_thread_irq_nesting_end.arm rename to ports/cortex_a9/ghs/src/tx_thread_irq_nesting_end.arm index 4dfb1075..b79d69a9 100644 --- a/ports/cortex_a9/green/src/tx_thread_irq_nesting_end.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_irq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -40,48 +40,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_end Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_end Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ -/* processing from system mode back to IRQ mode prior to the ISR */ -/* calling _tx_thread_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ +/* processing from system mode back to IRQ mode prior to the ISR */ +/* calling _tx_thread_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_irq_nesting_start.arm b/ports/cortex_a9/ghs/src/tx_thread_irq_nesting_start.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_irq_nesting_start.arm rename to ports/cortex_a9/ghs/src/tx_thread_irq_nesting_start.arm index e9fbc1d4..9c5d78c0 100644 --- a/ports/cortex_a9/green/src/tx_thread_irq_nesting_start.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_irq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,45 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_start Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_start Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_context_save has been called and switches the IRQ */ -/* processing to the system mode so nested IRQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_context_save has been called and switches the IRQ */ +/* processing to the system mode so nested IRQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_a9/green/src/tx_thread_schedule.arm b/ports/cortex_a9/ghs/src/tx_thread_schedule.arm similarity index 94% rename from ports/cortex_a9/green/src/tx_thread_schedule.arm rename to ports/cortex_a9/ghs/src/tx_thread_schedule.arm index ecd87b12..5ed175ae 100644 --- a/ports/cortex_a9/green/src/tx_thread_schedule.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_schedule.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,48 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_schedule(VOID) @@ -114,7 +111,7 @@ __tx_thread_schedule_loop: /* } while(_tx_thread_execute_ptr == TX_NULL); */ - + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ @@ -137,7 +134,7 @@ __tx_thread_schedule_loop: MOV r0, v1 # Restore temp register #endif - LDR r1, =_tx_thread_current_ptr # Pickup address of current thread + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread STR r0, [r1] # Setup current thread pointer /* Increment the run count for this thread. */ @@ -151,7 +148,7 @@ __tx_thread_schedule_loop: /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - LDR r2, =_tx_timer_time_slice # Pickup address of time slice + LDR r2, =_tx_timer_time_slice # Pickup address of time slice /* # variable */ LDR sp, [r0, 8] # Switch stack pointers STR r3, [r2] # Setup time-slice @@ -204,7 +201,7 @@ _tx_skip_solicited_vfp_restore: .type _tx_thread_schedule,$function .size _tx_thread_schedule,.-_tx_thread_schedule - + #ifdef __VFP__ .globl tx_thread_vfp_enable tx_thread_vfp_enable: diff --git a/ports/cortex_a9/green/src/tx_thread_stack_build.arm b/ports/cortex_a9/ghs/src/tx_thread_stack_build.arm similarity index 96% rename from ports/cortex_a9/green/src/tx_thread_stack_build.arm rename to ports/cortex_a9/ghs/src/tx_thread_stack_build.arm index 2bbdce05..aece98f0 100644 --- a/ports/cortex_a9/green/src/tx_thread_stack_build.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_stack_build.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ /* thread_ptr Pointer to thread control blk */ /* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -86,10 +86,10 @@ .globl _tx_thread_stack_build _tx_thread_stack_build: - + /* Build a fake interrupt frame. The form of the fake interrupt stack on the Cortex-A9 should look like the following after it is built: - + Stack Top: 1 Interrupt stack frame type CPSR Initial value for CPSR r0 (a1) Initial value for r0 diff --git a/ports/cortex_a9/green/src/tx_thread_system_return.arm b/ports/cortex_a9/ghs/src/tx_thread_system_return.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_system_return.arm rename to ports/cortex_a9/ghs/src/tx_thread_system_return.arm index 0c05f803..fd329efe 100644 --- a/ports/cortex_a9/green/src/tx_thread_system_return.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_system_return.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -37,47 +37,44 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_system_return(VOID) @@ -93,7 +90,7 @@ _tx_thread_system_return: LDR r4, =_tx_thread_current_ptr # Pickup address of current ptr LDR r5, [r4] # Pickup current thread pointer - + #ifdef __VFP__ LDR r1, [r5, 144] # Pickup the VFP enabled flag CMP r1, 0 # Is the VFP enabled? @@ -108,7 +105,7 @@ _tx_skip_solicited_vfp_save: MOV r0, #0 # Build a solicited stack type MRS r1, CPSR # Pickup the CPSR STMDB sp!, {r0-r1} # Save type and CPSR - + /* Lockout interrupts. */ #ifdef TX_BEFORE_ARMV6 diff --git a/ports/cortex_a9/green/src/tx_thread_vectored_context_save.arm b/ports/cortex_a9/ghs/src/tx_thread_vectored_context_save.arm similarity index 92% rename from ports/cortex_a9/green/src/tx_thread_vectored_context_save.arm rename to ports/cortex_a9/ghs/src/tx_thread_vectored_context_save.arm index 46244b3d..1a31d675 100644 --- a/ports/cortex_a9/green/src/tx_thread_vectored_context_save.arm +++ b/ports/cortex_a9/ghs/src/tx_thread_vectored_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,46 +39,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_vectored_context_save Cortex-A9/Green Hills */ -/* 6.1.9 */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_vectored_context_save Cortex-A9/Green Hills */ +/* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* execution profile support, */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ /* VOID _tx_thread_vectored_context_save(VOID) @@ -142,7 +139,7 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Note: Minimal context of interrupted thread is already saved. */ @@ -174,7 +171,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit diff --git a/ports/cortex_a9/green/src/tx_timer_interrupt.arm b/ports/cortex_a9/ghs/src/tx_timer_interrupt.arm similarity index 95% rename from ports/cortex_a9/green/src/tx_timer_interrupt.arm rename to ports/cortex_a9/ghs/src/tx_timer_interrupt.arm index ac7a6ae9..8f2a8b65 100644 --- a/ports/cortex_a9/green/src/tx_timer_interrupt.arm +++ b/ports/cortex_a9/ghs/src/tx_timer_interrupt.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ @@ -32,43 +32,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-A9/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-A9/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Process timer expiration */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Process timer expiration */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -95,7 +95,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR r3, =_tx_timer_time_slice # Pickup address of time-slice + LDR r3, =_tx_timer_time_slice # Pickup address of time-slice LDR r2, [r3] # Pickup time-slice CMP r2, 0 # Is it non-active? BEQ __tx_timer_no_time_slice # Yes, skip time-slice processing @@ -212,7 +212,7 @@ __tx_timer_dont_activate: /* if (_tx_timer_expired_time_slice) { */ - LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired + LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired LDR r2, [r3] # Pickup the actual flag CMP r2, 0 # See if the flag is set BEQ __tx_timer_not_ts_expiration # No, skip time-slice processing diff --git a/ports/cortex_a9/ghs/src/txr_ghs.c b/ports/cortex_a9/ghs/src/txr_ghs.c new file mode 100644 index 00000000..19572e2b --- /dev/null +++ b/ports/cortex_a9/ghs/src/txr_ghs.c @@ -0,0 +1,84 @@ +/* + * ThreadX API Runtime Error Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +/* #include "tx_ghs.h" */ +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* Customized ThreadX API runtime error support routine. */ + +void _rnerr(int num, int linenum, const char*str, void*ptr, ...); + +/* __ghs_rnerr() + This is the custom runtime error checking routine. + This implementation uses the existing __rnerr() routine. + Another implementation could use the .syscall mechanism, + provided MULTI was modified to understand that. + */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal) { + TX_INTERRUPT_SAVE_AREA + int num; + /* + Initialize the stack levels value. + + Add 3 to account for the calls to _rnerr, __rnerr, and + __ghs_rnerr. + + If the implementation changes, calls to __ghs_rnerr + will not need to be changed. + + Zero is not permitted, so substitute 3 in that case. + */ + num = (stackLevels+3) & 0xf; + if (!num) { + num = 3; + } + /* + Shift the stack levels value to bits 12..15 and + insert the stack trace display value in bit 11. + Bits 0..10 are unused. + */ + num = (num << 12) | (stackTraceDisplay ? 0x800 : 0); + + /* This will mask all interrupts in the RTEC code, which is probably + unacceptable for many targets. */ + TX_DISABLE + _rnerr(num, -1, (const char *)hexVal, (void *)errMsg); + TX_RESTORE +} + + +/* ThreadX thread stack checking runtime support routine. */ + +extern char __ghsbegin_stack[]; +extern TX_THREAD *_tx_thread_current_ptr; + +void __stkchk(void) { + int i; + if(_tx_thread_current_ptr) + { + if((unsigned)(&i) <= + (unsigned)(_tx_thread_current_ptr -> tx_thread_stack_start)) + { + _rnerr(21, -1, 0, 0); + } + } + else + { + if((unsigned)(&i) <= (unsigned)__ghsbegin_stack) + { + _rnerr(21, -1, 0, 0); + } + } +} diff --git a/ports/cortex_a9/gnu/example_build/libc.a b/ports/cortex_a9/gnu/example_build/libc.a deleted file mode 100644 index 5b04fa4ed9b6479f70979f5577dd0705d1f6d1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2447586 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

Q>dP6leU^u3WJIStz4({jFOv z9|o_G`9Q}YAJ?@w)<`ES%4ePa6BXsNO8=RP&Qp|SGyFdk<@X5vSCV4fUZp7a2lZ$@ z4Da%VOP0@Hy5v%^WwdLVGM!Ed*Y#e}GC5)FAyaBu603yWlOth%Mc9AAs+ckbBle35 zIZ~T2IV}GmzrSCGM?Fi1^wzUW@WUV%V=g7G<*o3m;0XvKL;U009S5-=Mqf_AFM?Q( ziVMCAA4SFBA0n;b7vu!*ir}CLB*+aY<3UuEVq$+&7(_%P36^`_rA+LJg7>}LXYOUD zO)FxA6k&qRdN@@4oH9>9|^EM`Oh6_gap~mjC8D!VDyrr1Rj?dk~({yiiW}^9As`{BDIOfiTKzn12nwZ27MV{Mh%}>9Vrr zzq_3J;T322Wy^p3s!eHTpqgI;uHE|G>(q}@gl=!P{5J~x@-!30kKV278br8Kh(c4e z2UKUtmj8G%&#-pVWy^n8AzgRmzwNo*lmB+$rcP|9Tqj6e>Uz_&Kjc4-5$oL2KN|V3 zKoyn(rTjO-7B0rCkpG;?AKNptUU2ZvU%J%De;Ckg!`{e$ESvILTG0YDAI^%m-63H-9N|d1vz2tDgSx%o3y?V?E&hu$-2Au(lrCv z=}MxbA|xI#^beer0x8IA=Rc1tySwwked*M>}ptJo1 zNZ{Sc|9vAf@}BP{e&`yG@yz(?P(x@8MbFZX_7(D-p&f6DmOc3{m$&ONT-D>JOTuOQ zQm1=Id%pH(#nFt_J{wFawOs|F>J_VDAtnv=l-crV zN9p*E(Rs*)>CWecayo7r{Q8VB!hwXOr%Ou~dPnitCHe!c*Hw|>*XFM+VG4xtQd6f6kI=A$XMjq{F3lrm$^8+2{4$7xy z{>N-BWKjY8R4kcLZDPdQ%@8p9sMa56p)QnD=O}ufqVpA9tSFxmrqkh`K`gz`O%s z-(D^7Zx7gad1EuN?;M6z$}sxz)!!NykIn2>KULG@r>}1DDmUB_B{tl_@X*@=3zpaH z$_0;n$h_}p8`$3L)|}LpNPkib(!Pv5FTcBc_p4rdLtB*E(Dn}OVz(<7x3eZn?IV3a zZBC{&>t305t8-luIwtHu3&YUvNw(BVa;!z?WhyDfGnJIQ-=Obs+u@{myb0_FnHLO}zR1fJV{XWDCiSbATm(ekXFfVs4|+n&S3@1>TxNi3&>u;p=gjy@ zL^CnfguI{g!utmQ_l@+ekovt7?2K*?+Ju<`^M+(6k9EVMxVR7DZ#{p>5#x^-59vD` zJah55nxjtrTzXs$MDYmAz<6{s@PAi+VdqTmTX?;HO>lL1RkSW=Mqio?Qup_FxcVA? z>`^-ARCiwZ`9}`geP;Cv2+oUFtX!3%2)%c0RZmbr-~ z^9)ZrvV3*T@)gUZ?Y%u^{{z_n-YMW<2(>3`yQ53XfZ4J(b-lF=$TvIF#qi6PwRa%Q zbxcc_Eo<)(q4rOw%a*m7?sl8Z$U4&yrt>hm8Xi7L+UdA#S-TSL*oH)1$=b)`5#$u9 zo#vM#>vTfSwe$JM)6X2)&8C5m>>TvDyn+|@1G&3iT zasdC4ocDer(gPAKQl$l2; znpU)JBQ_?r~{4k_jX%1We9s_^Z~&u70ox-!+ZEAi@EXyop$WNsWG zqZWs|-WwncHRJJ~gu3vn{$R{V0c2s?lJa&y&TeXK#y!i!FAW%?-7Q)M=b^k^j7#P5h)@AEE@@ z8gU!iT-Sm+O!U%g+eVjfLBGD-wma>$u&!epH?-jWls5|3c$V{adtQrQepSnN$lpo# zH`FD@I-LCIt^_E@hVT8gZFj{4Q-*eBQ zcSiT%7a@;M|BlM%^5FFv_I>J>wmh$~AmKHJ)rr}-=ZytX<8>`JqYclqe=2Wj>xF(~ z*gAwg%`oIa`c|ZE#dqMFUj3_WBa3cpi}^Rc&UBX|-D3`Tk$S>lP^;q8S@(I-`(GXPTKd>wabIou%prHry5(? zmOiK->+Yo*o7s*}!yi>%-NNtVl-Ky=CwA|Cmi2vQ_wMu7qaBfV^Uf2I2KVtzKmDtn zq5qZFkB9#+@K>kKX#w8?@+a@VeX#paYrJy=Z`-d(gMQt~K2a_`e!|2_M;d(w1cqBLrZK}V zM>8u@>kBvoiz<*KOp3TpQEb&DnPOz7(tu7iY9Ma=p>b6AOuUE>i~A&_RaP*Ur&x`# zywtH)e*{ZBsBn=GM`mCo(~Uu(KNCY;1A>gsf%YX5{u!4z*6MZ;9BcJMBk(D(0$0$o;R8d#qJhz-5@|ebh@GYc&$72Zm$fUq(=PI8D4@oun@g9?*7F zqBxHp(Y{#x5yzJw-@ZVmx$lZxXIF27{A4b z&m3ztpDE3;Ry!GYj7*vG61C=^!thPDUDxbVEjai2{$$69^B5;Bgp3V$rQ zOpjm3@_s7w&5GADd#qI}6We30e#yj{$6AHwR&CNgu5wfYg`vyQb2_dM2$yF9|se+8)rti~JWVX(;$bN!^4TJ5n`G+Yqc zW386Z+sUz3=c2ZK!x4UR4y$dCwfa5t*<-D~#Y%T_tkrv{mOj=hTpT1n$u6?TTK%5c z?XgxrCiBc=t-^ByPUS)I$Rt%(!}Ee7o=i z*YLg^@n>{+eiYt_>IO@8!ZY}EEGNBz#`_OUL<+iB@WUxrb)J9kWYo@|H}qgs%)gKE z{_LUGAg_Oa5j__STjTi;itzH`wVwY2&edLuC)IwPSpuKDc*>Q3ei85q-~S{S1~=q- zshg3e59V;ckI(oSG7ZYi_eG&+dLw#4EIWzeEsXu8NHO#uB!4Jk>1WBRGzrgapgVrE zsLI^p_nD}b`K8T^VW`_q$#xh|%kg&(KpX%a*1wICl0VmrF^u&S@85nfCGGl$dn3OA zzrTfvkMmg@iizl_h0Ad0*U^&usN(0@m1VT&?VopOEppHeTeS=|m_9%>zL$3x<BN#rC6h`ukhOP^kh=}-Lvg@jyjtkeU>cNoC733T z=8fYWVWy3r`<2u{6B&YvGHqD)8VH3&W>DPbirceq!M@;bT{0g~5b@ucePH{YtoUVc2)(iNO#Ip3e0` z_(;#_8(oiN0y|*i7&a9@Vb?oxz0Y+;IzHN5*AvFRuL{I^DZ}|duNUEk=k%Ro;YF^4 z((&IYPH$mHiIoI|2eD1ZeYkFUucU62{euKX@xH7DdX7rtcv=()I6JwqaZAFE; zje?hr7k5L1=`TEbUPDT3}n=Ie~p{_|ZKVrsAuhXqZgpPC5@m=k_ zEN!i-zxM^AGzK%~FzNxj->5fGJJ_?Mxy%=^8z?1Dj7SoP1aINz&@%n@C)^DfW z?EMa_M$2}QAKS+^kyhd7^1HXd^SENAoo@RD2yp9n8TgeWjQrRf&94sEF270$Ln(x5 zr{l8sJ3QyqFCU3DKYkkPJmgW|0lx;sx%K-c`0@U-e(mjdK(KI);KzHZoi6Kk1n^qN zy?}Hh@WrMb0TieK{t9y3pm&yFd0t#8tVJr33UR+hLtN zTke-8z&EnA42pzZ0WF8?{PasF^K*~3_D{lvmd|y1`gJ^Rqc`30AFpEAr#^c06i_Y4 z>-_XfC(C=m@v}XY>)m+D_}b5S;-%Wp@+-}all?&q(v2_E4`;>*$+n@!lov_0`Dv*fPGSt<-ah?zcS0;kmVlNLo=uL_ePGP!R73KUu|LuxyQ}ixHw<~(Dq7N$ih@v|beM-@%73DZ3U)rlCU99L* zMQasZrRW+(FH^K$(RGSmtLS<~X}6kuZ&Gx#qPHsAsOasAZd3FwMYk(@ucCZzc%P<| zj*;)zF83{^)V?K9Wbpz}&LO0qP;{E2eD^Z^d_@;4x=ztAD0+*c+Z263(I*uBxuUNs z+NS8+ipDrzQke4<<+3pS=P6pHXsx1`E4p4$Vu(z?P0{-meM-?66>U@WzZB&<0rQFT zB|*jclAy=p9x?o6MQIP6{soGDM$vC5`W;1opy(W4ZduIL$xRw}w& zQ8BJiuDIWzPpI(UEBcnA|5P-_ae#COD|)P=l)o7M8%1|1`gcWpK^|ayQqhr$PE>T7 zqO%pPRCI-+mn*tK(OVV$j-n4K`cp+;QFNE0|4=jr0g?3>sA#F8$0<5P(X$m@sOZ&- zeo@h{DSEe}&nx;nMQKZu<@`(0yj-Z)s0=KH}a$>--FU&A=F#fZRuV7D0kgpqcOkr3^=*kaTN z95(vFnX+fi7Ne8M_-t9BC@w&Iv&D#Y33{}}Xg=;~EU1ZdPeow17~RJ*vbPusG2L!4 zdKhWu1!9X);BGMr+$~1IowA~__%^aMTZ|rNcVuia3d|Ox<2lUC7NhsrB(ue6Dt%^) z(QQm=wiqQ@g4tr!hYiZuViaU-F$yxa7zIC+^_&|29=Vt;M*qzcGPW26W{c4jru?bQ zH!Ch)8fJ^p<7}VVVkCffyTxcRndbBt*e|meJjXwJ3+Tm`E+WKHAzJY9+G2Dgs^vw& z^T}zv4R(vsXW0{a6H$~bV667Wl1K7RXm5VeTa4af6YLhF4NPpe81cyr zoGnJ*XSB1$Xd{_9Ta3hqth2>P2qb%MF%sX+&K9HZf=zyaEkBk_&yY%w~R@y-^bdzs(aV)PXj=WH?B$=Y$zYL$(R?uc^M`Rua-g@pc;ndx@?-zwqkUakcS+qo+yZ01 zCBzmKz3l{@W44{(T5UVQmDzTJzBgM<`up5evUDbvc=puq<~Nac>}hRhi8Cr4!}wh@ z20s)4cM@*l|KQeDsLvc6L&dwQd?E=m$;9Hmi!k0o?F1mN%p?7;ZJ=m>$Hd2IpF{-~ zc448thIoE=_dzUNx?(F z3s$dOS+jgqg*Z(QiN(}PZlcbb-NL2w7lFxYwXNz>B>ca-t6`s2l6A;{Hk!R^Wf5}#0z~2FO*|Cx(o1g`Mm{xDTI+9mQM}8?0pQI!EYSmwbNx~ z?_ws*^nPM53wn!4fpbAN&?-xv2{w62WkY2!hAIFhW}7x+8- zosOa=@o`e^*pG+d$1q+qz7$kDhG}`VMA&xVSzcD0g}@~i@FCf15yE{7Nn2~Ez(}we z3TA9~fPF%Ip1Z_N`zYKLbPUsjPEwTjnSRbKqyjeu6}TzrVimqlQGuHxTzsK}Zd2h8 zDEfq=0yjmxz)eBhRQTJ93fvUo0yhN}xGAW>O+f{23Mz0@P=T9*3fvS_;HIDgHw6{A zDX73rL0`n+WP1c|3Mz0@P=T9*3fvS_;HIDgHw6{ADX73rK?QCKDsWR!ft!K~+!R#c zrl0~h1r@j{sK8A@1#Su|a8po$n}UkZV9-iDc&w+uO+f{23Mz0@P=T9*3fvS_;HIDg zHw6{ADX73rK?QCKDsWR!ft!K~+!R#crl0~h1r@j{sK8A@1#Su|a8po$n}Q156jb1* zpaM4q6}Ty=z)e8~ZVD=JQ&54Mf(qOeRN$td0yhN}xGAW>O+f{23Mz0@P=T9*3fvS_ z;HIDgHw6{ADX73rK?QCKDsWR!ft!K~+!R#crl0~h1r@j{sK8A@1#Su|a8po$n}Q15 z6ts@`C#b+pK{qMCz)j)5NBISA3ja&WFK|=%1#Su|a8po$n}Q156jb1*paM4qeJdu* zdsorjisoU`Chpf)(GjFbKTi3_D>_Am7xQ^QdcMP`z@C9e!k=b1;>CP|KTSDrF}!-! ziundy1a8=+{gtDpOppL$*ZAVKEF4|KIf*kH?XS?ibNaY966uX?iP{^K&5}9Ik+-4E z-`LoUop$$3!!Yw}Pj1N_-`HGOc4bSH+R_#!HoachxV9zF+w{7hK-l(%ro76ACfX4Z zHbd%>w`@aO-j2rRBk_3lhK-TP+hS~rjODAe95y4$w!ntPwJo%*$h1-Frkw%oxD?}u z(cb1K8k&l5Jz`fwlRpq)qZ^xxKu3V~MR<^!-^8>ziM36sDGQp8AKlOt_vSW#KlNPm zv~wDp?@c_{RD$%ocJIC=w+LmVZ+xA$M)E4xBAquVsyqp8II9`=;TA7_Gp0}2330;N z?!N2{sdtaC%#ZKyx*@haG2DNAf@^Kf6&-iDTZuYQ-X7Xv#Ux8pt(!uI6%(e_C@XqRPP zYv4b3&m?cdeSW&}9xuJ_zDeL06g}BmnAmd1h{GG3ub9->+V7}_mH^jYV%;5~cSiHw zc;wUR4K1O!Y1fe@Uql%CD0uxs&-4J}J?-5c>iVuRp0DYd0-#4(M{4c9d`Z^O@nosfYTw|AfoiyODJ zEk%99^mR=qT#q_pjHM8tF5B{>RC>k}lO{Gkp7yr9jJE!$EVcNFRC@7?L#8!6o;acL z7qo#R>pZu$IrJV%!*);T1#gT6KiCIt9e&gaEhk(*qh&1Ybq>K859?>Nq*1@n8}i0i zN^P4Db=v=BC)_VbUCSDqIhI(?jc!~TaVd^xJ}=5HPc6bK<@FURZ8_SRi!`*6qWc$9 zR$jR$pRru<=>ZRxTNEmw|jXyU!g z_ii%xt$fE8h_bNvvSU{N@*NwJGf;M3dU2CqHm9W=ec~k==fM8YG}QZqva$_lRwwW) z+i+r8YQu?qp0HQ%p|Uch+tJt>maS_-nzqUv8`?G_tT*p}Sz|*E=w^gRWz(Mk{n6$W z$6!+{`byZvnTo#l%TIh_9?G7c*w8i?GzT`B0@%5ki*4pFU~c7a8-DNPc;0qUzWb)9 zztR?^uWXy1`idBD1rr*ZIfh~gW8V~_{$=UUx556*n;08yhfQs4<@oHEZfMbC6Rh&K zZ@;Z=8~TmU%bBn*6sCXMwoTM=PHQavh5i`70c{$63VbGcTiSL{+JR@|NsR9~t!uX5 zmh)%id7EY6*%*la<#-wB<-ai&W$vE-RBOKryOkv(y&Ea-_SA_eLSxo{fy_tug5c=Xly+g*HIbn0mc)$^OkjGcn74{ z!A{ehX3qNz{~ydx)6s|QSN7>Vw5hLnt{WTtbk2?JgM5@%=6$}++uqoGImhXaEp42G zLT}?PK3|+~_>9fkv7zmpvJGuPJ?#I4-m5#(!`u3$@>^z2YiwSNv6=7+TX&%UIM!d; zF}zJan>S*--YUkcoO>E}^(%{7_+H^X#OGq3H+wkRRoJSZ8T3nDzV{`Ohw1!U`HVeP zw!Jw98%wamG>Febi08n4mgIB3e%B0+d$bGh_dK2MXyT|2iARV8K2E*2yfU`p2Gb=U$B09u3-7PtrMN{Qs~3bTf1u{^3Oxv z`kXi&ZHN6Y@^rf-uE5% zMKNwe%;%w(YKjs?O}%jMb5eGWIRf*p9Nz_Ru)KlZ`dzb7pL4*o9QBw-p4;WT%sD9Z znwlH?;XO{fS+Enu{%S;jvi`RsAM1~2;7LE#_)_(R+uCkFy?+zuC*FUI&u}u+iu&?B zGOsn^ZD{jQXNY1CU8CBm?>Fxe`&rLxd0u(*FyG6&6=j9#rsi^t{k(*n8<;ME`tezZ zT##3o;`JG=zIVn!IFEi1FmK?!#`m0;epbBe@;&CqJeOLJ@xLy4 z(g(ZWWP0|Ieiu}z`liumjALKmy>ITDI|fBO+DYn$vyFTn(PwyV$@!c8lkc6=oZbPO zVYtV9@8~ofS35m92K^n+?)(kP%Xsgo^f}-efv4_ET|Yd7gLIfqqvIaLe21aa3cF;m z&-dB3qj4{pUiQVOIcE&w7|2K8avn*e&&n~+dFdzc`)yks<@uO{{q0{se?HN6JLf*k zL(?&iZbh4X+;4wJ<30KHjm@`WT+PLgDcE7B;u`;y*aU1SSlx?|>@=EoNE%)VDF5d28zNHN{ zJ+7+JH>^V$>JUp@*%b5Yo66wFyS1$hWqaOP81rX5i9UT5b7gZi#$GSn=U(-VTZ7c% zrV#IEynoxO(FeUM7dKVzV4iuH&%IaKE{?CWG5$F&@j3G<+ZmzHb@?otVFOW?4&ylI zJYoFhc#kt*+3Y67C6WK2>A2~PXInTHri!|r(dspFOk$ZLVe>O=bDI5#=N$JI8)#ld zzoBk(TG8$tKV2UTuMY!kvKqv`O4Tp!AAt@=x-yho4c-ynkD`B4Yn#f@H`Qo!tn%un zYSl)}^RM!GVBJl<(KhDi^UZiZ_v}+0&w9;J^M=(#NF(0KHUkY&Vas3kO zT8Z&MUVr*v_e11`IgxSZUXmZf2ck~;`M`(LtDFnRBb}dqc4v7R`k`!H3+Kt#FlMKx zWk39zW3B&iya%?ltvl$-rhM<9H~RIl?~N4Z5dA*D7;4q;0qE4c$@!h-={b-6z|xU8PxN`#t(LXTfYyOhkW&Xq34(*`^s|l93|hqYj??a?j|{Jp{>l{-dvPUV-7-F zb)EG)SM*5}>vln_F6&`cN6t~tVGg++eZqUnIfH$R^Rb(})clre+)F%DZ5O_eKHI@Q z!@PiZ1J>7YPtFqem*beSu)e=yY=alaej)NN#=L|)t@<7v^KZz7EC+db&(gJPML#m0 z^JgMu$4WlFo5g#o9Amz+v0x#}p~@Pl+_&r)QwP8`@?f9qX^t>bkHW z*j|P+jQz;IXCJdq9>W|;q4}6EdC&(I54gcQ>>hjQbzq+f}!P?MtL?!1pNb z2jXL?wJrI0kKmhD+$*MIdsz>*iD|Av`*=Tje;DRfHa2fUSg7uo?k~paK4E{YVH(Uk zZ+x)(nX)wI9n^OS+8awZG{ttTZz{taTfJjL8=mPm1LxkqhcU^%!27_|n|*sf(%E-S zQ`UP)zNhjb_wt$Nv$9TnlNG$7pL32?{hVW5Nm(K#<-hJei~70mNG`ZrKYx%5{)l-Z z<#C?bxa&?8&OXY1=SLqPo%&wCt&?=C-->YmIUhlPBjy@}Pw1-ykaN#!9R!{OAtUxn^=jdF0_Pw1JC?H6 zNz=CD9khN|KcszUHr5~TJwW-6aqQbreGlk)jbp(59`JcTQ6|T;7(aN2UW|E@&yju} zIffX=?;FnH_-01^_?@t!tsGnWvd%n|lScl?o7a2{`qcfNjPTuc zB*KF6J6eme_7UR8u=EGJ^C&a$Jtn@(@I3H+BISh*ZKF}v5b)&uxH!Ef2Wukz!}@Fc zA635~XhM1HLwqN_+SRkmX9n+`JzIFh`@07n zt=?@Ro*$octaorL4xkvsXWzZ|DU}!YUor2^Z3XWq$P@SUP0GN`*FuW0B*OSy^Z9n4 zS3cj{P>y}Bw4A4(uXm2uaqa3!v~#wKdtgeA@?fJs7Hy4Q0hUg3?WrsH)Q07&0w(EQ z#a;skb^R@^n9owNo(VkWeso&!Ao~;L5tx@X0AsT-6HtVkeb^q#8EJ^R zm@(lZB*IFOTmbhrJ;zZFX zBA)qC^fe@t_Fsd;MZNpb)Z*{(qy5**8b~@%x9G|Hj=4T&Nz#8>q0r>71T=D ze-%AzlrtVB#~n>52VUS>Dw6`z+lkXPn3TouQmz?Z2MLHvClPgK`Gz`Hb|H$B$>}Mmggf z^cm%h;370aD`zl+qntsnQqBmZFTy8(ZJ7kk$A<88qG)bF6;9__y#jY!ykC6l1oOHw(( zSa^++kR+@(mVDWb%}+kaTX?L>n@ARehhzWs9_Bq>#io-_FtJt6c$*zLUBymG5%o6I-!&^X#e#`On+8T1nwLI4fI;&j2G#(${BU+ z5v!a5S(x@;<@lMAY-Gi(at1Z99Q&`|VXRfoSW8w`IpaC@y;aWmAC~JVXDlW!M>*r0 zjIR>TBcO^kPxGnO#kQO;OHe<#Wr!6qc=u5t$LztZx9WB>I% zmfwYPMiGh-kL^%z;7u_;rcln{v#RaCRxT#wJ5=4peBl7 z8EP1d0@~@0qF@~Q7ry^SE}TvM6ykC}!TJq89LwZQXj?3pKBO9Ii)1roi5Hy22rqUF z|M3%CeJ_VnMG*N-2p!rV0^66+1phEG<`E=pDng5+%JWAuHn<@gHb-6i=MIO(*3qMp z%0D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܌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 z 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

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�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

81Wqb<71CW_;{Ky2MBiI80naO763&>1l zs>^7b{3TtHnK+!~feAwI;pSl5VOnv27_6{5Sht0m_9^i@(_y)k(s1ZJ^-1C^Z(g< z7cjeuD(!#ob54>brwI*7BSHeFlORDzx^p1}xfnvgkOT-JTtuML-RUGPNji2XTpUJ` z1Vumw0WY7U62uEC@;S)KNSl`gUe=fLsHndJ(1*X={>b`pmCn(4ePAx@4Rdc}N@9D{^LXzz0=B zx_)V6s?sy4=W9&18fPAO0c9>B?}Pjf!V${3y4lht2ff8_o}N^`Oy2X#??_U6U#DkP z?k4^OzlQfp{?Qtsq^CD5CdEZG$~oYNYQb5`dwZ(AYhdPE@HT6~+1lylw$rl?T9c&j zP)DRolJQa)D0ts16SPiGy1(TOV~2z;1(Obz>RbGVF z%7k>`yeUmnzr47yy>Uiy{PM3YY?|Eo+TtGbJBoWQFP=Dm+Vpvijm3*Us@TEHH?ELE z3;Ha@{jZqD%Ca+x>*p8CQyMq4x4(I1J0tBh74H!1km#A=gD$aneFmSW^ye(E?z;4y zg)562OZi-}E{ctoH|DzZU4?fRuPkjGcHXG{P9=>Ck)YKk<&Vb^wHb%0dx6>CwpA@1 zt3lCdy4aztTh^{$>r%Fm;@s>aXDq)=A~&oH{yRa@<{mt2#9ps%jhJN9YY5C~#-Hap z7se__Z0IHHaMZ&bvm=i)_BtZAgKC3UX^46IoR)1iKG@Uoe`XId-fzAUyW)|Jtq;l) zf$TTUiymjh*kB{hgZhf;UpU2@*ou0`UCCtUI%v?mM9V|;xDZ9A8?~s_Hkn>drl*o=Cse7r z#7YA?7}GMy^j}Q-M#p8^AI*;a&ov{~p}r8<{T`|}RkvoYb&+c=cnzOk6qs_2jCCtJ zy6yHs%||Lv=ppp+XdOUn#-0!O)X_{JC&=skytp+ea`lW|e*K8QLT!tePQN04WBB(- zwSL7|66Wzn8N-j)TcUEjz_!z`cv_iJ@X^CaQ~7M1gSw*6aAke8iMZcZmdbrSO7NR= zs>;d7o!+WG_;S_y70Wmd%^~SH7*XcOVS_wh8T=?;i_#_c!Efpv9_=Q1g2`tcm*fe3 zzqGRL>#&)O*(>8SwXNX$xmv&CY2>L7ByXfEC%O6_jpbd2Jhj;epVvu3+`h4u&_0cG zeEZ(Twy(ox@;&5LwBdr!+dw>)H?flO)@mOVM=npCtm>%k3cp33d_ef1JpFEQ=+plh zL9=`$%KHr4oYJ*E>jlaUMsH#qgu+*RrAVIkosE>T%y~FSxtv7N9sBT8Q|tKYSp7B1 zX7a%P%(iR81F9XTEVjo1X`5E*>=B zIAEJAC+0%sZ*}-thvplHbn}e^nr|GimK)pTJ2c-oq?^eeXufg4r7qum;}AFBIN+Tw z{a%OW8;5kM-b=3e#sO=%NENp!GthkFfaV(qG~YPjldjw!9GY(&(#7<{Jkz-#DQ8#sSSY z4rsn{K=X|Qnr|Gin^{G*cY{NmxLEvFhhKE~Er&mJi1QFDXTEW$Z@zKB6L{fDp83WB z%{LDCluQ2)A=jGu$N~4@VXX3F9ZnFEeu%@@J3PT*v%@xr-3~8yc$pA+?{@f6ho5ly zpLX~om;SiJ-wDyn((_6C=ardYC1d(uire#rxSnr{FLe2fT-=^p{CU)KSmnCf`RZI1 z6b=UR*)yxDscEqK1{3uH=CQ6eamLYO9Gz(0jRda#?xsh#?16ukJ>a`d=K#MuG-qVI z=7u=;|8;r>*S1K{KwSCat%O&>{cb5G8}FAa412ycIY}P#dJ0X*FujUJ8;3oCUcv09jo3Dy zPPzBjtBoAv0mr!g%g@WKrkm=UXeasydnZjB>+uP*zi;19*p^O)`bGO!JO4jqu0sc7 zT^;?C+-N!!kI-iYSMJMf`#RED;L_V)b!+e!r1oliKlWF{?<m&5kS_tkiyJERo!6zjpS1Yik z0CKg0ngDYUXrjz3XvpQBgK|l32FuMrZLbODG$*qTO-AKHdiYU@mkE) z3O+4CxmtnRvSVwcNMS{5q>NI{T&>_%#j4c`KCc}IwE}6l_P$2SBn}_N ziOkgsM9KHHM#@*zbGcfxTm%QRYseVZut*O_iTAo{5f0pV7wF2?L(HbdIU1E)t>})jEFIC~7R`5f0 zWKb(utK6Vgutd2*t>DetZ%`}vmUa}>3VyD_L9IaFMXZsM?RQcAIz@w8L8}S}wF2Fl z@fs;_mFP&Vpj!tKsTCY0X^~pNfr@XhRv=xec#RY(5ZM|j_5cWK1r6GLP%Du4o&MHH z8BgnMjg-wQ7}N?@OH5EJD5%l>s1;12xK}IC8Yyy%!x||KNSA7fKFp#uQuG}et&w7j zE(Ns$eWkKSO4i%+>c1gTL9IZ#`q3IGCrffrD_~Ha6oXoU+(txeq;yDBq*idWWJPKP zdnrFsD|nw;5~&qDAaRje!A9jrY6TmVAE_1Gpm=|31-DXQuxbTbBju}Va8N7wq4vL{ zY6V=XuhzZl*GRcm2mKPM6%?N50Mp^6ZrWX#riFP97{{>Z4tALykm?L8=#vB9emPwI ztCgLej;_M5YnYc=pxBRTY6(!8V7#tbI8} z=xUifu|+E5P^!@R`=<-qryf3aS+REh)V8Tty>?pT$Zu!GjZC!qrDh1=KeK~3Nxd=oerT=&{kU=P zJhN<(dwOmd8ntNG7}TPD^usM?R?w z7YaM>TPoff|Mh%jReGPVmDLZ7c9nv{236U(YPSJaY8j zQhk+Un6n*sUz@$4$jO4rDpYg#eU;Pvvah3hDtBd+;5X+~m6Num-iki>a@E}ZPdScB zG+xJ{(Um`r#ccaJlJO^o%T zJ}J-5t{>g!KB&C-Y9AEW{q2L|^L+$V{H&_DCz$ZkmAyA6nkR(i{w3na$-(bI_v)4l zaXN!LuH_fEtdggu?v6F`0TsUjw|^_L-^2N79h*9iZ5-VFTu{CamI}F`Rtfh-eOySn zhe2^_j|jQ$ek9~#G>(i}d)=1>ctjnS(sk~H`WBE4uyOq0co%PSXdFN3b6osnhsN=f zzS6}vIy8=-^bfiCZ4U2pc(23nJABNcCZp93WT--Q*;+o6SevIP>jpGN6;|Go72aV$gjpGN6;|Go7 z2aV$gjpGN6;|Go72aV$g_vf9W{Tas(8pjVB#}6)b`Nr`RH;x}Pjvq9RA2g01G>#uM zjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9R zA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01 zG>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uMjvq9RA2g01G>#uM zjvq9RA2g01G>#uMjvqXM7rw3!~Ip(wIusxC7QSz-69hIPn*L^UjQI;FW_+qnQK~hGG8o> z$W6r?sb8!d&;N*zkkf~$cqyw@xz(A%ZI29GqYG-d7<)-%wl88_6n8XI{Ie6 zvWV=kmc)Oi#KKwpP7o^qrBWy{B+d|rs5*`3W3wTNqZc?RD5qFn5nq+Y|iQ0 z8}00@d8M-0*I+8Hw(OXx_?aB0OvTL_HRp6cr^ZKXNnA~-;zSAWltQSPihq$Zy_<@= zwq>T`S)``CNg{JjcQ486!&Kaq9X^-U+plbf#yO{Zz3Szh?oV}&a#QhFYY#c6Yc2?S zaJv1Qif8*fru->M$xX#yn-c58RJ=(gAI$14EdQh0lCLFEuXg69;$~!@bGoJTRMeS@ zt9oQAt|)AWjGjw!^VO5n4Ta5A+!Y>-skkc|OvQ6iJ7Ow+HO(?p@%QWS!depIFmq$@ zQoShbJX%wKl&*Ep_}11RrNUt?iC3jWgQ>Wvs6tpv;;kwiI9>G)rs8URVZyBXJ5)Gu zx(BIn;B=R(R9H*meB}mC_y1_WfzutYXjn_)k2-|F=}Pq`G8NaI8Jmg^SLuk;eNcOj zI9*$5CE|1|%HN*TJz0B+OvQgiF*6mn2SDI-=crs*OTs<{`Y{!MfnG9Gap{#rrs7Xa zOyG2Xs7Ce8>6XlUS!61HxTKv`(7QvN?np@srsDQJ7EHx8WfYl;k5k>Smc&Z+XW(@I zq{6{eoUg$oG8KPJbpxk+g6alS@q<-3;&i{Jc*N;8tC`VS66+)_;&c}&KjL)l`z+#g z%_~C0>B><`f2QIkdG8o3rz=zOq8bo5-HnP4!0GA~Y-cL2d)1qYOO0ZPrs56A?$=bj z^d4H64nLrQu}&&NI-QqVCF)i9c?;|lMZBhq#;sEE6ZQOp*F9jqYDq&RU1GIVL!%^? zhOmN*t>B{Mzz3+7TWBZd6DAp>v|Wb3-T4fxePO%^gY17!(gh2quDJqjjg+qHmoHVv z!Fb*BRfH5v9x5th;nAKB=3XpYtIwnz;{gxbi_+N^>E%4AMwCT=TDNbLs6FNVft) zfA-Pt7S}2W&OCZ&nKpY9|J|m|c;|0vO{R3O6wR!yuJ%<^)@gN!DRzTQY3BShKt{j<4##8$^`9yi1RQC}HjTRy`dMsr_U^T3!m*@xAh+@k_zKDysR> z&ojTT_mAf8_5M;hUOwCLqpL`ZgUTu##!uxLURda>oaXv`9Til$zl{?7=A5c>S|m?z zL?3)P4R)jCA;|EXr&h^(hK|y4sb7S?Us~Drb=XWU;YHqCKVLvz z+`i|KS0PRE#DRHv-E7D5K94;0sSiG{lZ05_evATq`>2x3tL8`lmCTA426}lL*pB5@ zkf(W9wU4RYTwb+p^B<8Xk7qteo_@DD^yy!MKZ^Us@Aoql;9AkdoQ2;~?m!;_eL0fz zYf9O*%-0TvADsn%H5(z&S#~^Xs*X#?>aS5YlS{Z>j?{(=e!o8MF;0Iu`O$3uX|g99 zGkjz6-pLYP3O-bQrBLA~{u3HG56YX!ufk8y4CTkys?~Lq_TD%-ywUMD!t?S%#6i8rKLK*9aQd2pZQ2+5`i5t*d8T zBXOH31&wP2jcWvrYXpsJ1dVG1jcWvrYXpsJ1dVG1jcWvrYXpsJ1dVG1jcWvrYXpsJ z1dVG1jcWvrYXpsJ1dVG1jcWvrYXp~a)2SbfYXpsJ1dVG1jcWvrYXpsJ1dVG1jcWvr zYXog(7BsFAG_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@ z5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5 zG_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Daet`Rh@5j3t5G_Dae zt`Rh@5j3t5G_Daet`Rh@5j3t5G<#z3d0u9P0yhs2YT+0m>1Ia^PIKuq9UklQrLL}W z4Z3esPxl`$SUsN@$2XCFY)}WoQ*K90cIwJhw>x5Tooy${ ziD3GF$??SGvurzJ@;I(yh{<151Y+`iJUcxxspU{3V)9(-IAT&$%zY7)S*?;D))6r& zVlfbtA6IPr5_XA=U{ai;sQIuiu43?%6O(%o$$VIsbiX)a@+<6I#H3Bp#>S?ed@PmU zi9jE#Dc?>7AFD0fThNh~gC`~*LxzaS$JOR(4c79c{4d&1PE49h*cn+~P5C2gd`?Wt z+&2)DMq%c}bt(vz61(3EJAdvTQ_dudWkO#V?~0x@Z0fB}ff z8<1qgr1fl0OshM@IXwiQsF>Men=e|h)Eks24d0{4hqEN3hg%# zllSO?3dH0mBq|V-|Et1*n0$+(ftXyX!ok>7cVr3hY3|#HW^CF(^ZO?z|Azt)lT#S$r25oOPfe{7n+p{^f2>wAP3Idi zdDd7~F(qPrmtJQ%=3Q@#F;yCilf%>Kn=joeq3KJG0J>#W5~J>3YV% zYhFk1M?9uHJ2@UxEoRFPG2g%jtZ*Wnkd0<&Z#OV-zj<%`ryk|^OwhP9Q)CD9f$hBA4j!) z=ewC(IK~U1oZqbGFPB98ppMVWYmMr#nLJR-yIUJBsGLGIe|aAADx|A@{qdKz%*Cot zeemt8<}WXc+D8-e_EqzjMX#;UImBHOZ%cE+w|>Leyi)m1vz_F=uJ(`I&P%O7KC?;B10I_*-1EWnm>%`a(sQG$opoK7?FgSat*K&R z;B}H`^}=VuU2d&|(-ddv`AqrFUF!Vk)Tt^%ky*%s?!~Fxzx5ZU6XVjIi9)vwTg^r(7*IkU@k8NU@oQo;h9Tm zkVMR7Bc&a4c@{Z+F_&4b($N%&m`iEp1m^M-#l|n;-XCY28=D>tdUfKj85)s}6#eutUMHzHBYrTK2mnM-52a^`X>Z4V}uGENQ5 zr4b~#N#$(i<;-OZT_2dsKPfL~ExN{+o6cm`klg zxdY7Qhb1y+E+3GrL77zkK@H29%aaw$nadk>#pcXqo%WD3ms$*QD9q)pl9Dr*+q8#1 zOe)PRH)k$w9GWwi-_!Bs%;n9B1?KWR2_2ZZ%#;n{)pr}HnAu-y4u7{Oq5Zn9KgQLc~H)KAu#@X^|OW4r=$`2$2oCY2Yda9}PklH|Z#>VPBW z@?$C-m`kk*-M2~Q87vzlzoG1ix!fpW z5p(%%NsE}vit@K-E=v*bvvR$d`(_Jc$;j!;ACinBke5T&WtS(j!Sv zATOH|@C4@U%(BuP=FYZ)OL&Bd-a6oPRXaq|PDwS3u#_Lw)vX<#Rr|mc{0-62$}2>m8SVXc+PW_73?*aa{hnMg#hQMPcC@Q$)scvtIW)#A5L3b ze@pYNm)~CV6>4hfdUUs;WyVMLvLr26;9w0chjxE@@5nZ_(Dn*n(sq0Pa1b_{;U?ep z%rrBf#ml-on^)oudleJWYmdm8`QU?AB)#14`s24Xt|@G8$G3BI?VLnso~JXli{@_I z)9z(x7oRevW!<{2j@I?-F!3CG=#A@)UkD6IEL3xy703Yb0eUBCgrai1c(&s@Zz3%Y zDyvYZ#nSD8X;esVb+poZf~$_;MOWN6E=74x&QibsYV1olD_5ukb=B z>o=>p&T}FzQT^fjr8TO$-**B{r}iM+e~K~h%o^t;6&VBt0JQX1R%;P+e2 zbzVTZ1AQUTm#enbyo_>R>7|^?bBW~Pwn5rzJ_*-3Gp`l)8ztGMZ;v8>F0S*uyisv$ z7=^xsT&KpSy>XorNLQZDt@2+B`k?D446`Si)7j|dwp^|P7|QxVS8S@8bD9egA#PL0 zyxUfDx2>klFY(OTZL7)sKtKGY*=m|!SdPnVHNhe;C2@@-98MRK{yG;w&f#L0{$?JD zxo!h5R*hv8KU_#$;||3=m+4W@Dy4Vfc_%QMmC8VCu^zbtYq4I=JB!f6MpJxdGTYf^ zvNmX`>84?6(+P#7a?>!@BE?wf2fST3k4`EZhk1UpR~yUS>wFHp&gUR(EX@%X*MZKH zysey3b@y7fDsSEXTvPXq9ie^WGmvVmCU>y`C8Po=P1zjL#s zG}w^7MuV@b`I+;#Vp7QVQ20;n()oI2{mv^87}oE+TCwpvz^EmapIW7D?&3e&^5G z&A{t-{=38kVzO38JpeKJHCkoscUsTp#H36a!}^_nFF`pmskZFc`kk`J()yhbs%B11 z+NyTd>vxI@8szdt>1Z=`mqNwS-kzD9B*MlU3tBllCR(Sv|>3i`Lxbd zPE5X8v7DH^LG8&-FWNRX42PDaDLv{m%Qv!_4mK6ITCdackTfg%G6%52=v%~~q@;{Z^H!)c-&n%9ZWMRm9>2^fx zcm7P(12HL6#%TS{%Tzd+R7(3MTE9~lV6=W`jp_zsQmQL`uitrt`Z5rcCrMO9Oiq@p zh?tb)+hVkS=UlZUA|^LTTtrMxSAJwtd7SbiV)7Kl`ynO^7f@iZ#H7~m{G1vbh{^l4 z{{e`}26kbu`*F#BH`OG`Gg`k>_bR99hupTWwna^cMohW~$M+DsSLts4Yo6dw%XHcv z3n+%Lch>K0r`>M-&I1`gr{81$=|p1~DPa14j%6Hg`JRtVOrIxt6UVvh_-)R0yPkHhf!VpftF683(#r9RPpM36YC7UA z(~tS7-iP z|Em1B#!r5RV-~jK2H{8;NEj!?t3T(Mq*$gnqX!Es9ePlnLNzzoS2@km`8ukna_2`0 zesfM$IgPvYrt`s<)A%n+P9||2YWx^L^M5*C9oJ%hz7CtoQ_L6k=0CQY8=OQ(slMb1 zy}VYoV|goiaZe)62j9;cZpQ6vLZ0R$C66ZL@-$f#%ex+V2a@K4&#ShBoE^1KV+Y^9 zYHsijGAmvf=;djA9k*{@)V}_>!S5k&lRro&Pcm)0~+~9pA z42Bzg1_Q`C8U!SJ@l#XP?>bh0Ewc&PklDuBHh;cfeB5K4{&H}Gy4FI7_pbKq$_Zg- z*gZm(ThOyYT%VqvKB(Wa$#b(})WXXeXodxt$yR&EQc(9?VOXXg*QY%q*N)jEg5y;l zY;t(CL*4&MKiT2=4p%zd=#~f~R_#2139V~xehZ7x| zuQlqKr#aC4t%9b530~{UnW{AL+gw~<)Y{*D4u9Zqn?ucSDBtW6LG!l?+E+bj{#HTr zw+foSRnYvcg66#pG=Hn0`CA2L_aeDw5&@dORZv>>N;iK`;7k`cf2+hbv7`K@4%av| zFIl9Uzg6&Fmu~)6i9hY)=5Lj_ad@C{c;Io|c#`{wL-V&v{7Dx#f2+jJ9uYKutDyN? z1%pQ59bm~ zYl1)$B0t(uHdj5#+Bt2xg zHW!UxI^&QAZJy3G1r{=`jGeV|Pglp96}&^TlWkKcG}L;ecxbwyu`pap)m%<}&V8uG ze(jrATl@G+?%=0R$^ zImdX*T4tK)9)6CK7A_Vu30`3<>`Eqwbo~K5E;yO)w?J+nD44PM-1+}>OM}YW}$uX z{o>;u~N@K8q8R)yS3}Wt}xUa zSpRD3%o!C61N)TNybq1wHl1NGDcP4V*e3L^X3fU0ebc6`<|}`Z{Nr!hk~D4H=6&LG zI3qOp5?9tgx7+VMnr94Shw(l4Qu)2#etqs};CxWe;q@_GycOqe zf}!>6Yn70k&7+mmNXQ1)^K6?>ND|X!MEu}d7Q@cn#D8mZn0cM(-`#}n9p`T1?~ylv zyNS1Imth6(U#jxB1~mbv?q@1Ceu>3!lrMD>=WfCrS$KC7d{uZy`H{N``A;9f-Guqo zeo5R-*kZxc{Bd;CcfnEqS`N7{cN4GXtr;BU&*l(}~FD;wk0z;cmhv zRC0F{<}Ez9n~)|(z|z+a;bca8kB2w?YO%M&0Y=C-NaZO z!cKBGF+#F>Xmu5Gt*&ErndOf1d%ByrO7(KBu3xBRuGLl6InA}Yc8YLyw|03NOeY%@q=$1t8CO)OYL95H0pa-q4FQ{;Elwaj;;se@m(CRu=(LUWx z{EKpfR+sL~*irtUlpSexJ)%8FT3yxdCMGF=d#x^cM~vM~{FY+9xtp++zJt4o%T%o& zt*-x|m(1P7uhm^atLq8X3tC+^|J=7$S7`uu6MIWq(CT_v(cmcmBpq?k>e^c^>C@fB zFx3rOUHWj1+)X^Jy20IqdEW_IU6WNf((1ZT@kpy{xn%d_ZsKg^M_OIas(z%^Rqbx# zF}0>2t*+9?xEThk)uk1@N2&oqt4mgbyR^HB;%VFr!#>bJKb5YbvG~kCg<%g{{2iG- zEF@^?<6Tb7Fqac&j+2^jIzw7w=@|!h!P}&>jzn0x=%DoTl5~{SPDiT*!>jA)$#j41 zC`}W4Fejrawv%+hG9%%k&1O084m;V?M3Q}#*qo%-;i3zgZJlF0L9mU5^M%?$Yoh<9hM&Gw#(iI!Q?)9tIHD8##LfuHbDE|?5zOp&ye4-iu z_bpv3Ftl&!Zd=#ELIRqvFJuU<~*EF|Z(y^}D`lFZ0@#eOR)>-egb#`9ZL50?qZvNX= zbpqKE=_^`RuWmt;Zp9k55`62f?{3GZ3JwjDe06~}TCbUv)9G`0=D)PPrOTI0a80qU zIqU8$clENCOF0LvT`bg&1au&S>w`98D>}Q#@6+`I4=se##P*go>(@%qF1$E5YppEn zSiW4xzuWagSaEw7TBf^K9wL4t@MgDA%|m?rRbhTyqXw1J3%{C&xXfyYKb2Le<{|nj zr#VMoNA*-regJ*&n{!C;+sZqDm!RH&KKQ(99^y`pV=#X1FG8W^2rmSoezTf~c$kh- zKWGk9==-HLs>5b-J2Op(Yr_TK&(%D{kC0bK8OiI9hxjS-dYhN8<{_Sl+DDbVebqd~ z@5!u??}IfFxF8y2@l z$J3Lw*ctj>(uq^hvqQWD-INFQW6Y5$o)43qs|P>u6eigl-$twQ=b+;#a2~}I4yX&p zb*6pE?)y-OGaMf6P|phG>)sV=HchzF;YNq13r6~fT>LhNcR9S*q3MEA&c4M#Cj3p_ zZycH~80n@922SL3Xn&>)2AVDyxYnhcE*Nps1p`eN3^ZLZ&~(8-o4N!|7YsCAFwk_t zK+^>SO&1I_T`+L>+S^(G?_PU5>xW%s?d@lHVd}o{{v4`x!4Bdfr+oA00G{gN72P+y z@AZz1*V?w{1%Dp(+)=siww5)^cZ4^bG5EE$FBjdy>qE~RX&Q((+$&*iZS=$@%Fldq zqv?y4>aO2bQ~AiY+NSqB9{ZQCgF8I%g~lzaJ<8Q9QO?TyvaF$9>&o@3XSQ!r9F=K} z(W0PqHq50DGmW!+&1ptBBle7H)TC)TT1EC~KsT+C?2$QXN2DCe&f2>3ByJ-s9=f9 z7w+&M*oHf7r_k`$^c3ZMMmZyLj}Qk_r&KwfoDp{^r+1GKu9nUGdhQU95c1Jb%DF>X zj|Ptr#eEdb7TLa>kx$8UhvSH39wAEKwYHc?h%4EYM~LUCRpk+)fyUuuqI{OJa_;bA zD)?A!c~TS`yhn&nP)i;m^b#)S+~IGupXq+DKT+O{ELKzg2Q@xlWLsAK#hF=NT^Xk- z7`SJ#G3G6!I4g_oSN>n+<=o*1B&8>J_)B&(ut$i!R55sj&_ikS80Id^!BVmWsxW%A$= z;_Etu9qt9mW>U>+fUszVRlVY_SYL(=hoIw zQ{CXI{(clr{Fcl8mq3+DsBgC;19dUV8A0p15$Xp)<31z^uryFr<{3&_`T!@?r^5+1@2Ju9{qTPDE$X}^{(pW z5kmU!c!bca3H_7$f}(*ttdsD-9ezx?fjg8Vdpts9M=-Bm-bf;k5Wi4v;0_N~vwJSG zT@2izKF1=D5UV6A;toq{N5mZ-to(>OltyXn5#kz&i@3u#C_my3Pf~v55yI4N`r{6l zQ(>^&p*%u}A&Wdhd|3M*z$1iC!FC=Ybgz1k5PzqG9-2pp24wRB=-VR%*D)8sVZURv zURx@CgVOk|KLRLpi1Rz(M8-hrDvlz3ja{*lrzst|hgc0=Sg~~q-di&+V-F9}4q*>l za8N(#IRqpJ+$0I_V=w7!lc3rfNqH*rD5D}5W#W#?#Fd=5<0El%Ok8D8+EUCHJP;x$!ZO2ElmGhDGea=T7oEAsUFJ3gi zIC9FB#XUbtMMzW_??8enE*N!r@HQVOfAx+l4&oKS zE8gb*^g7UdT>N@qd}IOost2ES$$rdjkfymr)mJ%q(1)$|@YjPvv=;TIj1> z6S=;Q3aZ@SMG1a$PF1;scsc6L=z}j;&1+uIK}@3YI*#G|{BbO1+t-nddzpQcW}**% zvzpiZ^4RRSBu~fZ<+ZZy>qv&y+g_p#7gSE6n%BGsc@@$nj~TsOUN_sZyrsxHlr$fF zUNx_|Ici^jyym-+SMkC?FK+|eQMV>fBCn1#wXZ*3^V7)twm(QGPcmn=i)VI_{MPdbR3~yO7f$ z4AA^zfaV_qH2)Z&`NshN)0O`phkNkAQ~fbQuEG5rE^z5*x%jyb-{;Z~*L^^Fz0V4~ z^LgA`T+b%Ok8yE(p77^U&#;U$ZCe#lrUTkqPMbBO$xJOP1KV2u2o8^sbEfq?Ddp=v zy$Acs%Fmv-AX&T>ETx+_6_d@IMkjTfwx*l!`Bhr?j>lg}PT%^+7hd2ovv@0h97d*_ z*F0WO-t3<}F*#YX6}^=Gle(X6Dw1GS6jIF#O_D}D*_VKiG z#p7wRcuTVR-d`c_E582kUU*?7eeoSeHCySzk)_I}Ex*K$Gffsg?tSc^Pad|N`)j@M z|54uFY2EOB_&<2m=yH*hRe*c#&-X{n)8YXng-y(eTXCU&nd}igeSp*iB2t|99wQxd zD~pcg*Zery1d)qLa<~2nHd*&7HardaH*Dh{qy@k~h#vCz2XXKp#PARD-YoG2AomZl z2U5wpRcRyWM{18KYDkt2A2y1-!g+Iv41ZE7=j!j^RND$_3a&!rHZL77{~+Wi6M4g(c zyvb9iOr5c|tKCI4yVvHY@yx7%w>`Iw&BI0d|M|??#B|EE#;KF1H8oCNSuD?=+SFJa zJ-_i^r?M<$v2{vg@raKWPn%zyIi+~g{Nl9v%(thh#)KW(J3d>J zi?(TnU|nF*_00w^zq6Bd(kq{`tYuwGGNrYp~J@ zc?n|#-##{Tc^laFbtL1x$g60>1)ukJ;<3CMg!uOH37yNkfo;`k;-?_1_IfXbxHk3s zh(o}FzQHw?@xfQoO92sUefpm$H<<71v7A)BXngSUgxQ-l%NeVKB2nY2z4)o0bzDkj zzj+wT`QY;{4_;oL;5Op)fxoZgmS}(R9u-$>LMYmm8plW2ISncC-c>(7V~Xp{hfqv( z=0Yf155#fxLm6R^@LGo-5Ypv;@9^JT`c@bJqC*>F({+A~ZO6l(NA-%0so`tZUMi+18(9zJt8RMJ z%t4Q-&(Y4f95r8{Cx*t&0!OAx z_)q6sDYop+NY5`{lB6f9;_yW`mQKI1RKC>57vESq~e059P%8oVde9VU%(8lOFDb7v4d>Nzp)$ryVe?DM;FiD#h{?rEIB57hm-Ce}1$Mt=c zJDjqM?k_q5@H&#NBSPa{KjpAUYsdR%`u?+ndNYuwCRDj%1g%C@f~8K2~a{#R|d z;Pd7ak4H`SG2rpz=KjX@8|ffDMt$)8S3RzO7i0ZJJJcPW8HeB%X-cCH0_e9jb{>Jt4YSQe5AVZt_eZ(PPVLm5eFys0wC^wjK z{U*u{W?a8X2StN4mg{d^zlrN*|E#I@A01MZ8v0*}FUPokRG)j-y%?V{#dYRG7|OU_ z+@gY>0ok}dez!8Z&)+!Nh^xSP(ED>RKHU#hISNbmj*Rbh@2Gr>!<7#84XpHw9R97t zjSjDI_|Fb)U`P4QE^Zta@#hrhIq9PAwYNZJ$5-ku9HZ`rei=JoLzx zFWmFJEq4;XllbTFd34L?AG(LMN6EWq%kpD4Z@shb(JlKlo$z?W;hVR9>F9g5jIPXi zyo|rZVfSp>dglT6Z29tG_iVYF^si8U6!YaJY?-{hQ?2Zy?zwTBzDYEfj$PDKYEHl3 zA@Rq@ZX2ilGYcwoX#1iOnAnKznZ^-~K9^+e6h#km(O}?o36BmgZ9?`Hb(L+ybCa0< zL>IP3e0)CrK}9kHr+*?D11G(HF>pGcC;u>K;G~5Xy@6A4{D`Be zzHTPIj9L>5Q_IFQ=LSy4k`tUGzLmBI=ZNyfTgVNZ4p$pyHXv1ttS(UAky&g^`OT7& z8#rkV&O&bBbcgbK8aVxc-PELo6UtX8FL#dEs(QgWqKq#h=ZH6Ij{`VI+!qPv9MO6< zH*k_Os>0b>V@qWX3xab*wWV;bI;c3|s+11Y%9Azk=kJ63tOE~9_D=UIDQVAt=C4IP zTxc&B)u7zK>Fe4@wSm)XC>}XSypJ;H6+Sttd2Leoltl3*J_>!~!fmc?GH@zifmvhA9byji%r$0(^Zs2r-V!46S%_^B2I31`)=gtxL&;j)@a4LLFW^IKD zO{1D|22=QY*5+BnX!JL-b{ewNOJkJi@D zSK(mb^mFw}FmQT@3I_wHYg9NGI57_37ITI%v+6&m!ok34q6!BCr!Ex^22N{~8w{NE zREeA;{<{hX1E>GeAp`@b52$c3aMGO_J4d`%*^z@tg(Cx}jfzJGPMTtjog;QjTD0WttCSxZINhoGk%804w7bZ_={Kt1 zpMjIUeFtmcB|slHnGDyPYoyggHme2EO4N_Qcg)lZhu(2_1S z#SS=*kq;J4{EbPqt+19KMoC=^Q8SaIabKruy0?`*{Xm&=rS*3+!z{nHguO}+6FFeD zL`_x6Lrs*#XdsoIpVdFa>Mu$TGB=sI<%+X{^3KPzgNj-~1B`Vh}R z!g($0Sk|$oLoVXZT-eE~Hn?<)T*H}>&#IX-S~53&a)O5+KW|+`PqeHGPW~`AF~4`X z1;poHP-A#SI!VlWl+*`96>$*SPu8B&+Sj2Cw;p}?9S{*bcOAxqr{<1VEbFJF~90*nrRBr#&kK_X0~C*WBL5NO@VWLwBXNhS9P@L?kydTT>>wszs2Bc(QyWsWLL<` z?sqZP3eU}+V6^ZRDPNQqx5YMv%ep(8SK^~`70xAhdtMBRQoD{~ltzGh&oCUYpn13W zCE;Hk;#u5a!^&OG%Si7d%^9k`%JDMV&OYgG(&C`93WxDi`D{Frsbl&!C_j5~ImTEC zE=Znq?|tx_b4c*p%A1D(y&-+@dE#TDVwbI9CIwM zW?a_qlBY3l_V;WmfAaahBJrv2>eaH2!AUn0HL z#dU3~oY^mddWI@)_Di76Sb$%0>GwJOfkU%jBLBB8ZuU#W&3*|q`z6rqmq4>$0?mF2 zH2WpcW|cv+Ujog32{ij9(Cn8$vtI&hxi!>IvtI(uehHlG(#?K}_)-@)`z7LLzXY27 z5@_~IpxG~hPrLGFzeL>Zmq4>$0*~Vw(SFT-2{ij9(Cn8$vtI(uehD=DCD81bK(k*0 z=fE#Yj@d7POI_UTmx!DF5@_~IpxG~hX1@fQ{Ss*QOQ6{=fo8u1n*9=J_Di7IFM(#i z1e*O4X!c8>*)M@+zXY275@_~IpxG~hX1@fQ{Ss*QOQ6{=fo8u1n*9=J_Di7IFM(#i z1e*O4X!c8>*)M@+zXWa=k@b(+FA=}h#m#<+__ti#?3ajdb#b#_B5w9epxG~hX1@fQ z{Ss*QOQ5aq3;q``Wu1=~9FF7xFOGDS!-|mn$qtVaaxKhv@iT>#f1|^tE`5!|@wz|A z*E>Y^QF?wUez=P_xwt)V`17b|n#y&z%M<+acE6t7fZS@+j9G)_RzJ?Wkgy%M%GJ5G z&;$qi$SIE(Dw~Q)Wz*=`7i6D0n7CIwj&V^jm=%>`8g#dR#~vnBUV*fK<}*1`S5kJO zxR$u1Y&%J=Br*M+F0bqPLlrb5I7?5EK752q6`tp>G_3Gx0D|*M9&r$y4a9QSjptCf zRGCN8h>u80@2(r&9tzKEmyX%Qb>p=NERC~0vFzJTij7}_C|rRhB{^5iTsM}^<9;a} z+(-_7nY030=DM-)MQgV)n-{Vv*Nx`REoL^0hh_3h<%?9&#cImuQ^Ch-%VVP0;9WOf zLoK;(+(gTY)BJHfsQpa$d)524I3tVIls~|c6pzSawdJqV9mSbhtgiewiXEB7#+0=h zO>tHh+pny*Rq?1SHnuEQw)na%R!Qm(rJ|^rfn7IBIVp18SgWHRfY}uDX098pXLDxr zY)LvhYeK1PuE%oMjcUt|xo#9cA=ix$s%Fk?E>`naxP36id^NeH+qL7)*Ri>$sGVbp z_U^jT)yrHrW|5lmFp12W&B^M=K3q3SSHAeUtloa*rxnYY&EM-x<;>%nXmdoY{D9?WL(8`=JjDSu1*%b87cO4x_%#(T8C2eWz$%ZF)y-^pT&%HL5e zXErrr3a%RqqAb1ZMpekXF&;^mj8>HXK9=pmS1Hz$*$jo3MTOtY_88UJt0dNNA317E zzsoK&xwhrxu2=pil%I>D*851Pk^4NI<~=s>S4e6eisO7hiHvv~Ei%`Q8j9h%Q9|Ie z>&5Zr&KXPf)8lAOeVh6sFq<0Y;kq%aTUY;DWJInTN2%`dF1M-vJQWVi=J(Z+b6xJN z`c=ve%;qBH24>Toh0b?{=hnYXI||I^V=5e6H-1))Jk1qeR6jw{z-(Ti!hzY;of*4s z)K_Hex^b}%B4Re5Rer>5N;#w#*Nunia3W^&XvCQ7MtcARX7dp>ZVx+!a@{ym)%syJ z$Fnzc-S`6)49uno=*V^Bv&!w8*)05wy?SO-t{W>l$g>>FoYZSH9=UEjLBa#G`8E{} zt{b~`1n0Wmo>za1awBF_xq;a{QIh*~-FSogGBBH(04hez=8Xz%%*Nr9ua-JZY=0C7Xy?|+Fh77g{Q;;GZ^-yS|2Yx zjn-$w9(ih9o1_aYZ_!whN$CPT^3v1C{e)8K0_`b1gMIy)Lr5+qt&l9$|IRS;Fjk~* zmx_b4zwV2Z99-?4z9?M&d11t+xist7CnF`YmEq`h(K%IX#CR4F-rDZS z;+z)LH_ng=&5lm^O#e9zK_v^vmQ@*n-D6R@Z0$byNn^FEm#91GLi8N7| zwy@d|_)zD2(U{}EHkXTz)Xiw#oHxu#^o-2R2-WU&cqzTGc~y%Xpyo7m%QDzSubR+< ziEmrq)z!}Gc5V5pbv;(cTizk|(Noabf_!@J;iNI>GjM4%FJCVfGbgRr*ou6asO-FF z%M@z$!bsU6zrwOI*|K_Cmye|mahve+dutHO_{#U#&>JrqzXNz3S*YeE=b=f>3&rN& zcqJK=qWHo56lZ{A;XH>Pl&2sLP2~qFr+GYIhfVY8DD7q+{N@}I{I>GGO+;@!AAFvM zh*5H~NXNlJY93K@ggRceYcW4xhs`8q{!MP~eej#&A7Xju&{4i$gkD}N+rExuT+T~; zk~UoM{j3pbEN?0DG-s*yi7WE*s(H!VkXIq!2fwdsUUF^JK8+*1ymztf>qy1}eaO=g zJZ|45QTv#N&E-||l20PZg&_ZX+Y9K2*t9x~p$ z+OI1ogq`6RQGKJJYgoMD{uayrEH8gvPiYfhm`AmBkskTkoc&k|>%JS16Xg0aP7qZ8 z%bu#q;n5ED3{d*X4viBezS6}vIy6p@^bfiCZ4ULZr26+d)EuSak2y3>khpPzpmBnr zae|<6f}nANpmBnrae|<6f}nANpmBnrae|<6f}nANpmBnrae|<6f}nANpmBnrae|<6 zf}nANpmBnrae|<6f}nANpmBnrae|<6f}nANU@aHEj^8*z&^STRI6=@jL2#)nXPh8$ z;{-wD1VQ5jLE{9$r(JpD1c@6b2)>G&PWv-X5HwB@G)@pSP7wTqD`%V_apMF*;{-wD z1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5j zLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L z;{-wD1VQ5jLE{8L;{-wD1VQ5jLE{8L;{-wD1VQ5j!4qn-^JbhNapMF*;{-wD1i|NB zIeGaP2j|y&J4A8vSM%7)IX%)mr)SRx{yggW5;(lU+egkkYSy4Ryi5Aw@M8WhJ$lpD z-5eh8mz}#tTOS-=@>*RgoPI7sd&f<(ZH5dars9vQNV|`c-YaSMdY(bmq@A9pgD362 zq+JHmPDX%%v^!O?K-xVm$+q5F=}j~;C+)sSvA#*W8`48+mR6Gs>PHc#Ok2ab;Fy9v5A&Fzn7Qz@6BaZtKJkPz7oTzRStm9xNiw^`{J=7j%IrEL+f`%! zWW$NAtWiVF88H}H;5B#qq{wnM*v5IH$_ONJiEXReTh`z(qj`19B^NK_S~5f9ZUp!x~iWFk4b@nYk-cYcHO&<=MAW_&MvZ-K-o0Xjd{tZcCv`lPN>? zw>VT@Zp@#Nmy^FUAN=MV68yIE?joW$oew^*nwJ~PafpN&h?m>O)YDtN5UTjiYF=(^ z#CtIfne$YwQ5`murM$TPq>|z^!9q1JH!_;n3xd+}4cl(oVRm+#Qc_zqpf znqf;>)4l^4fC?BU|Bfq2K@|^EcD;xVK?bi<} zws_7gZ?`QTDguM@LmGJ+9oN%esptE=KhofU^lU~oOnm9o+1qoaZ8NCSY}g@0yaUp2 zkx&{@dM%|gKcvN9X>(XnUb!&SlLs*vKfM?;Gd&!Tt|n5<%=F}htq%vJ#Y}YZGVL-r zApN;2k84m9Fw>J#NpL_~`jRFKGsf>UsujJNo;_R50clYVG@S#|Z?Y)|q?%&v!vU$x z=e*0&n(|Uw>tnU$o9jfx}4q$4oJIbdvHM7qW$C!NX=R0%&d!=wD!)fN&CT@GmK}3IdLyS^4oJ;kXKtqVSv7uz+XpkU zS5vAqO2Px<_YaiW0SBZXmB`#o?|;zhL4Nqq!T3VEN5z$)XNEWEEB;DB_wPEBC^ z{+|j5Gd;AIlgI(-qpEvB;Y&)Lu(tknsvFGoOwTf6{H$2S_`Ol>YBl3RGt;|J(jqgx z`O1&X^q$skA~QYNz{U6gKp>B#|UK@A9upXP@KazLtK!5$m<^JaQ- zK&pGyJ0Lwp2RSq|y#~bci0Io)?*QZQq|lNcl3Hmh9K#Qeuam~76ZEV~&m4QN(z3`| zW3Q%H(g`Y#W!|`jjFZx(N}86cs>VsadXrUOlpOd4gcWlCqB9ix%l9w3P9G;P!oTQi z(#DfFY$)z=MPqv-pM~46$*M<@59<24qiU~9-&we_c+RLgKmVfi!!570NW9-`*ydyr z-o%M}qK28nb~QB7r2s>cMU+35UsjT9zy+OLA7YoH{kRexGE;!ZSebWao#;%rcCJHG z=fyPG2AK_#Hw1I!OlD97353zo#ckcpvtxz?lQUx$PC>B%TAnz|L;PB3-`pKJ6t!Y`!Zu@mc8mOZ_+>l1DvYLAs$W@!f znWW4dDmXLcxM4+`VA{;$%GQew_FK-4D4bVkcl*xpCYoQ2)|l>g_#*X1FK$J<+TA(p z;a)U0GYkEZlYp{3>=EI5&dzmbO#k+>{;y81omS5bx3+M^8L7CUW%cS7BTd;#ntK?k z!SlcDZbjYw98;04b_I)noYS#Fy^yn$t6O!6`>T+PovV@y-zQ`?d@_C0wI9)?&Yy5b zU*@)!oGUG%*PI&z2BNfYC!(^@Y~Afkbk|&Dxi@Ywek1tz1QbvYPN<0&RfROYlX&&# z98ZX?#d{^Z_cSI*T8E|Z$lq^xoU235yzn~ zpnxRZs3@PIxY9KKOpF<_51qUWG!E*B>|d z9P(c4g@Jxw)!g81QTwQpx38KT91rdLCWz|0Y zZgB`$_~RhC!Rgpk9Ox^7lBGcP{F+kybjp228!q^A!fYgzWn2d5C)+C7?8#h1P1SMf zSp7B1X7V&#vaU5BygVQG7^nX^xIuAuvwUOd9bG%eL~-RWi{kkv8!rWyXnY~E)dq2i zqU3#0UW1Q-icjXJvKr^A{7ldXla&u6F$k19(QJ^5D^ZOu<@r4t7=>Yn-=aXq&5wl> zP(Ky8fR}({xenAXikpoPz8`g;^tsj?2A=kf3m&R+V3Wh69qM{f`pFK@cev8wMu(dA zRXLlF0&jEiyByx@@cRzUmXLC0O9=kPrJF4w@qM`nCC?_!K(i$T&6W_<^{#xgB?Qfu z5Hwpt&}<38FS&B}IW${B(#@6-G+RROd6#dtgv8C35Il|>P5UuhLeOjp!8Vs}wuHoQ zb#b#LByP5ZpxF|FW=jZ4M_uyFmJpoj;$}-o+-wQKr7qoU35lC6A!xRQpxF|FW=jY@ z?aG@iA#s~u2hElcG+RQ@YzaZLB?O=3VWIYzEg@*OgrM0Hf@Vtynk^w{wuGSB5`tz+ z2%0S+Xtso)*%E?gO9+}RA!xRQpxF|FW=jZ~Eg@*OgrM0Hf@Vtynk^w{wuGSB5`tz+ z2%0S+Xtso)*%E@rU4mvy2%0S+Xtso)*%E?gO9+}RA!xRQpxF|FW=jZ~Eg@*OgrM0H zf@Vtynk^w{wuGSB5`tz+2%0S+Xtso)*%E?gO9+}RA!xRQpxF|FW=jY@#S2;I>%WCu zb6Sx@sE57M_jI_wL-Q+2x?CEm+$@K$cloC{9Ixx2e7y_B`7*SyxIGVu&v0>Xd+1Tm zGu89YF3)}rru%u>12^|0`s!rg`hxPwBgB_TX~$0RM~Q} zXiI6)M~>k6{kpX2$4|VlsQ5T%d~4ct-xL4)!VCNR`Xi9@+}<~Dvwb4#Z0Zjqtro8J znbR_^)t7yaH60&mfU9k~fk)mMTUn=XQ+cQ-!4JIZzM*j%W4M~{7~Pllx%e>Io=OrlK*`$&_YJdxDhu&vbe5o9ge zT0H)}zv8mtwS3<;9v|$3eD(M2jgRvf8kscRN4tJx=im?Q^H|!bzU!|q@2+HhQ>w5J z`lFD(_gA;q-Mp<>$?QtC-%lY+ef*9uSwtUH-nT8;q~l$@mAsLqrhnX)Zn~Ot zcE(oD7wy}$RsF1UQB(Ks#}oR>pEL31I+kOcJaOjSz1#MvWY^8ad_NnuZ9zai9peeE$e8lw&mOhQKFYeb-+5m)*n{ydR?02yv2(d&6S zvy!O%{K4Ss-S1Ztoq?!mCDE@Fbt{P;ug%{5?Uu4yrRfNYh`>H*{Nf#4h?GU;Gd4xN|zkW&U>85#0eI|Np3IIg$T%^f@aR%sauk$<-Km;2kjOYQ7~$RDP>oXG!%>g7a! zxAvYB`Hw30=r`?%SV{B^YUqGOK8~+-%kEVg2kv8ZRs$0LoJ9UV(r_d4<#{_=N%Thb zL?H67jH5O6rnKR6YwO>wx`D{IQD<05bgAkFB3}wag+SyFS2V07daI&=$d`M9XeH6l z>5K#--zeSSZ+CCi4Me^)mLekmF4YZ0{=>=*M859KcqP%*I|uUh{%`AQX}&10T77%OSStzrgy$p4AN1S0<=HM(yi zzjV2ZM?}877(^?H9;#?C-8Ei65c&2k8i@QoBr6d4|E{`$$T#h*K;+x^a8J|Sh{!h$ zj6mdnNTMPl|3>X8BJ$5renjMN)~Shz{QGq(BO?F%%8!Wrir|1Cwc28j!du-q@Ch~Q!dLsX8I_Q^($S)mF52eEoxSpYPDy@ZdX4*ho z6h5rz`D10kn@*j?$SFN%>>8y_wa3}nWA~?!i<~v~aLZBM^vrSptU5<3GK>RZ67_^6 zon@xJOU6krFtt~5`lfL+IkqfPcyCEGL2_Ul%u;%W2|D+{MM=8E+IFTC5+tFbuYmMb zs&`GQCTdvbn|YNrb4k|BtE`!4O2)M&V{x{-_eeIoOB!T0pH?g{9rsmI(}Q0?de-K> zO3FyOIg~>3D*f*?*TK8snx@%==8MrfI3itsVbg-f1(O@^K5gU83yV{ipVC-7VR>=% z@{P|P-c;P@imxvGNzDa?iKBmdO3kswbLJQKtT_h-1q_g0qU3yL7>f*agR}FjD@OO^5vS#BR^F}p68CX!6 zwQS8DRX2CkewmuXMy?*_4BY5`IOBYM@`Y*L_jBT*VEt5y$&oxn5vc5f{~}QIPJVit zsn8`^K_w@B`y_T+1k2ZC=E9yI4axvR@Fbg4?H9Kh2kR-=YEHHfUOOz@szn*sHZ#lJ z(IqZ+b!)6>uoF}>n!C8xsuqP5Gc3A3G)^qDob$FXs~AmgX0Xj`u~=+gZ%n!|=z-pc zibt)Y2M@oK%&=_{*LFP3Zl};e*F;9W-SSoJ{3Scu-m~RZ6o0-jtPap<@x<#RRH+wa z)OMyH;T1%zk9wAqW=W>(k!e_Dsv^DVA!v`i`a90Ac())g&64Ni{4&^1-NWm=aOTYU zvDa)d3U**OE+W?e0#dENgFFzoNONwY97LqF6biW6ko;IPMl+?bNL^ zg)=dA%8WRBd6%@Pri6uB*RF5Xy~Z9wS__Zx6-vr>+B3G3%&e2%bSdKZkbgT>>rzaF z$i9d7k*4VNno>Dldd4RzZsRm?HXB|B(R}-0wlyB|;R)oazT~N{m)FR)(!D(KWxdH0 zPoe$#pmH*%@WF4MM1tQ|-ft16H?|KxZw?7@`<|wKQioOhM)33PJBw{!ht1?!6bdem z68z?3@?v@GCWPZvx|i3=w(3mdr?3j5MaS=h$|--nL!Z8ma_9I6sB#K#;TP}sGWJ^` zO~;{GNx$EBvhCZ-X7VudnzZ49&%1(nEbqh7am2b5;)8u1iFh7)6>YfS^Yld&%lkO; zC~EB!`u2UAZC{7YWGqS(;;MY`d3TYZZQu48lzY~9hRaj_y>1(@Flpad_aN-+A##h^ zkk863r5u++9{hf*bvWKexqr<|1$io8h|=eBW6dT($D(thrs}xWC+s&58zJmJn#Y2? zD9JW`dlcDm9gbl=#1&1OAS4y-%8%oF*fpdSwL7K79Cj3niTwM7P%XclaJr+ATK%i? z4oWRwW9}?u`89dR#7C?C(iI&-(X@NwxOz5(qG`>`$Fl37qbk0xD*m>rxb9;gB=1@u0TsW2pZ|tYdjo;n z77fr*PH`jDY_;2b#1=+$8q8~nf(!8*r4g&tan~$UBBN=`T))O=x2?wa3`()9NKp1_ zzeUwG%3SwW4NqFzI@hAJ91Uf%`(i8?k-o1qLoi#&J)>ux;+G5gn9^~M>E%#Q&tcWS&7q!;ir?$-`wky-xXq#Iu;>PTNrk4YnUJht_IUr=S?Z?Ol;-;4anqCfQdO4u!<$$J_1DakA z*hU|!ou-!q-s<9}mqXn2azN9|0gWvHHP%r*)5`%(F9$Te9MJS~z@@I7>E#f&c}URo zazJr&l4E)~;L|Q{dO5^RF9+02lggQ14rp_spy}m+rk4Z$!Id+;9O9;z1DakAXnHx| z9Ih$JGrb(p^m0Jc%K=R<2QE(c?mjjw!4rqEgpy}m+rk4YnUJht_IiTs~fTouNnqCfQdO4u!<$$J_1DakAXnHxI z>E(c?mjjw!4rqEgpy}m+rk4YnUJht_IiTs~fTouNnqCfQdO4u!<$$J_1DakAXnHxI z`S=F^gF%|kgZcR8+S3bM_xA`P>0=x=IFt_{rE852;TwdUb2IlQzRaa}33)Aj*u`%Z zBKK1c?{Mi~b@)Svk30OG!#@hSChh*EKD$T?JcR7IL;M&QpDiSA&nx~s`sWqT={K)l z>$QC7F`s+xZ+^4cvZ~H;IYy7U zyYTNhmHZ!=)z(dw)f?&>YRW2`0rTj(SfL8Rud^`*`I+PTgGy6=yw)FFT9$$$lI#AL zWP{!c`lpRAwEiu8S$W`xNxU5^x)aq{bzvDRqzp?fSSFr6GnJZs(X8n+rlSDU8yXwb zu8`B~=FFQjy{URSrmd}8r>}2lWdS399lHz?s>`h8<@1`8RwQSZ&X`kr>7t6SZ1^v2*|uqOWfdsaEQD?_woWGuW$h?t&6o9@ z8^;o<3A37$cQ<1)IBqM0UywO~U%TD() zq|1Ja>2gYM(#@Wm5k|Wl{>GSeNmV;lJ_jfOj-1!>B1`#9)7)EFodc+}N?Nor@(gdN z?cVd;%+3kZ^BvZ6WL)PAFwBoKuK2TGH$43(8#bfnDUWw1GJZx)(qA6kFn}+m4_WvR zmNTO0!ZbOTI9J>GPLa9A0QPSajmA8>J6X{ug<)F>-&eTl`Q$NfXT8u}-gBUQWv;d-z3+p)Cu92t0_5upC$Ih-hV$GHNH!+j1;Stn=w zijAhLR}(9e}<=Za>lV_)jm4a=zqeK zGx#`me{u$Q6zJyUjLYeD2u{v;iRpB6a>gyp{A^X?{P;7dyy)bNE#&4ZzY*~-P)c}m zMlreY{$I~A(mHIWO|@2HG5&?jfqGoKmicYe5!&Uctz zHz#KlF=wIrU7FnYpOB|*6_#cY(Wu|C(RFij2B-I#>UU9K>V*`M?d3i?I%u?yPAIA` z`{-PW9LdQUa{fSc-2JJnC8J;$$krwy+v((tb6IWO9(SLhsOaR3Y6gzf?|#gRj84vI zp>L#qw~W5gK02f18PglRbUN;SF9S#FcLzW2{yYYb)bIFY9_-|db6E&W{qA?Dwq6`} zKa<6=)bAERjGUYy9YCahH;Tze$K6wr&_9U!-2|jA$K8vTb)44o5O1xQ&!`a>jl}kJRr@r0_`njvATWpPa!xwz@w#V;Lo9s^9t1J~~`nwvM|$ zgQ6_;yIIVKrGCdXG5@`Ma)xNtN9uPw*@$~kzbi%+WT@ZqxO+aU=Hv`H@2P+4ce>-~ zejgok-2Fy24|UxAIE;DSuY-3NxDp@vn}`c4ZangSzW7(5eZ|7Z)&R3J2%-qc#Z*%_#V?W4?wTOR#mD2Dw59K><> z3=O^N%*V;r;_8p87Z$(Ytur+2S3scO1zgwWnx-*4<2we1YiAe=gzKo2G>_4Y7-I0Y zVOW}}xEQ~ua4Cc_abOippP!x?W2_6{VC?k!7chM58DsoTfP>}iOhY-Cd6t9ge=INa zwH%j;1FQ2Y)(y8?4#s*q{4(UBqD;Mz8hKT)n>esKtML+_Op7+Ap4Wi4U7CW3X~07KJi~yRJlzXB+Deo0`W(VRbt5z;T=qKt({Oks3>C>^c#L>M zb0C3wh9k&9&$g!m*2FMYB+qgZtH}?Up4aisQ=p`DUPsSfXJ<=|jE_6Yx{r*VI_r%s zCo+B4fnu#jhB{Hkg>}Jod=4dzVS6R;8I2mSG9U4@%IWrO(}&N}(0VU;K1A;YRP4sJ%@MDRHL8OnZz;dK8IDwy<$%gyWkSsK4%r-UxMT*zeTnie?+J~;gs z*WHD^Ire;FKi=H3FO0hb=V`3PzA{bN6Xc`VX9ch4d8s@04{}!QT>$&%PlUg7b_e#I zxyh~AwTpYiI8J*J`bpWtgL`#6ntFNf4yULisMx)KB(7rk?MgibS!Z`VntFOK#!~3G zS9Qz~dUnT9_&do@><_T_O5u*(`;W(7B@XC_rrrA~D+XD3lMMDj^HOat_J1jgrLac~ zbYl*DQ0+P6LiU^3FNEPK+fD6~w4BuX;W5M+iSYYn?~+gKFD$%8_CjG;=81V@zJ8+P zFD%@>&qI7-m4mP^$%rKL@$}vp%2HT}baAyqmN`4*VUc@IhkI$8&;4=2eO6GH%(v8s z&d-ww>&Bz~UAT{r^U-$3?f4zi|Eb85o7BbrCibQ#L`BUhBoRP*cz=_F>~CUWqGL0+ zJIUVPWU$`uguCk}^>!xFxg^{8fXh8ncq*EtmGw{UOB>es^ z$nm~{Ah~(mI|3{0@W}D+haDT7+=gg?Rv9JtL--4V33(+593)qO%e$X}i|OISZouEI zxCYSz#TrAF0H~iU( z%a4B;MfB$=t}uQIxw(oP5f=z?p5jKv_tWoK#f^!7m{N{YT+%7zwlU~wpit*8jE`n| zi|E8U1>^W9lk-bC3jJqnBF7zi{G>Ye8%ed-%=^0z%@DkV=B+{z#-F* ztMrbKKgaZ*d{E-ek6#MvKlN?l78c;MT{L|qWl{Y5Oz*oY-m>@!Oz%0xRmA^;+zYet z68B10#!H#&?>{ePSmpTt&VuF)CvxU8LJ><>M7D@5>RYhN=OLfa>STBfZ_Er#)&wSA zI)X8V24~@aIBURt1!*X`V$*VW(4Tb@rHfc?aEP&tVZVVE`D2^MG?8Vw5jhfQ^%)j@ zuBS@tB+fxtV5y?gToNP}+iWaxG3&zk<|nEd_XHicFoE~HqJI4|yk}3jLDyoJ2eM?cRTi{~I~D1+49y|7CpN?EpDq z2g05SM}bzQ1BkhF!_%4QVCDwNEyPY#i61fAU{}?C48oYs(BN47AMqXhO+jh`t-j96 zEOF^ZG150p)NF2IFf%>L{WtN=cVe{gl3v4e{NEr|K&yX+dfZEdR=K4FpjEz;DD8I? zevYoOAW^_ft@0S!D~TmUvE^KkT{#}r#8oW8dAhbwP5cW*o$nP$^;RTaq3>$%I?2hZ z#MzX5f%k+Yv>}nt%3Gt^ro_j|rZu}Yv4*17dI_o89d3d{XF#iygulj3@G}FyN>Y|1 zO3q(aVFp6yJSR5W=8traya4#`g`Y9@L)D5_ymn+>xDa< z;qnu4{|^3Rzri2dmT4D{A%L6f9fwSLGwE^E28@8*&mc{AgEUf#;3!B#tJUJaatt>X zaI2*kTs8JpWY?``YVO%%f62hr#bnMI*NhLK+&b|&cYFih6z&}(;EzlZ+)Ij*(s-Zw zcU|WR_;}75{vXC^r;CF7tkUw*@-po0U*Eb8jnT{_2L`15IX0%L-j%|>B6m&ohm@Xn zYFSw-H7zyEFJ9sgT7o@F%I2)UvdrJGauFknC_sYE8F-#J9=V>ly z^TTCU+~|fGs2P-#6+=nJ^bGoP!wH_r(0Tiie>m*d+H5;rTVJ`p1^aKeq^q}9Vk41t zU=*l{5q@~khS^4;51V!zLL4{~zU9UtecG(!&|tXltsOnyA#~1_ZtgpiaqLiZyhwFx zb8}6@WXk zZ)s@M2fdh6O*S>6*HgP^)ZtrPbw&!$1%ln$2;{xL1BmI)-g7&Fh;l@~b^!^$NP3T9 z!S zYK*ZifP=Bqk25E}35_v+C&0mSc2=VtlaYCrgJVEbj%+L#3!2U|MhL*-nYc8^o_ zKzXbWBd^Me1FLgCz`Jr?RMeIzNLv1PzVx%f^E5 zu=2-ohLM+z1#|ECWGF34YZu(?{M~Kk4_OH1Wn;k;Aa9Q;B*HO&yzbHzM9jXT-eJL` zQ6XKGml&mKdlWbi;T{pm>W^vCkgD;Lu+BOF5L}rf7R>r)R!Lb_Ti(l~9EbPZ7$eW{ zhT86j0t=P}HK`FxD81W8%8yShV_^XIaRNdv-ngNKR1hCn8lmn zEnc$#n$@8x0x;Ls*0;7m*^JwN$(9o>bq#f!9ksPY#z`vXwTn=Wvl18gj`=g`aE!50 z4xXp7iWF_=GEG^h9vaCVq}bGieMXP_H{ioV(aQv#!0@2cHGRLPr)YYbrpyn+ZP1j@ zJNX+msGwX4q?`kq%09o~^YHM|{RmAbX}Va`<(jV5v_Vtu!@+o8*7QM5 zpVssznsVO_hLi6&KquiLCV!HqXJ~qXrgBy*{I_YIYxWHPxTY^?`U_3}psDQdjBrKh zFc^MaqOw%_tWt*Y@Fin%1mT7u|rl)DTTGNf1UZUxTHN9Qa&ujXirq5{l zvZfuHzNx8?2F!dEXnqH*oO`3i}(=TZHkfyI_`nsln&@`7TV@Nll z=|oLuX}UnuGc{eSX``ke()0#RKc(rHHGNFe7d8E&;Mj;a94`hgt{tK298$P1)U;Z=H)@_h1;e8&ad=8Vk9c=2M*U6OxP_u%-aX;n zGy8yd--CM(6XM+o^w{2vJssXKcc1I*+RMEZy8Px;e`xAk=&Fsn<{SxpF&@WJ2uL7z zh&nR?$RM6}9ZZ;5se59+I#1!~{OFK{K|mcX5OQ#IssgkI;>gc%RAGK}d;&^vBr8>w z1vd3!5f*#FhPcsIvKf1& zM#)A2viF)5A3CmUY^}m-KwS;SEw%NHm7CKo8!DTy=(eqiGx%17nKctjaB4r_jC4Bh z(%QuePk-*5x$R|{_X}LBv!$_log*2IzEP0fb5+_blH^O5OT3&9E?jtAaxyA-N>UbP zlasNpWev`!osyiq5K9#+8e5yIYnrzuv7JZKY)Uf4If=t3y@Lz76DJ4Wb5|}pZLyQ< zVJE7->=V-6z8~G})cIbRqjT=fc8^!5m~#Bv?bWp6Vz@ILh9+VbX=(`lXpY8(*IAF@ zrXh@p1FJ)MpQS|`<97mhW2e_IaPi4A##oPo173%r$;W$;<59VzpezUT#rc(q11k=b zP#O@l(-^-cu-o$f40()Cd3eo-^47p^;=t;(Ltc^=ZA|^02i}$!pl@Y8Qy$$-{y3+$ z<*_1@a5KjEZG?j@Zv^C-{IT0M@^-;);!sAi8+qH|XUiL7txu#=`iSju(Ktz*q{{h1FX70^>QXVvsK#e?7>D09!$5`~_*a*u6 zQV!NT%ZhYEa{vzPyNxmO3~#9I-t*ks&I!}AQzXy36q(NZpX5oNqY!rLjBnpD^1Snr z@nu&Un`aF~#?N@t8Gjf?Ys#>WWIvV6-Wl`Z=*7c6qsRFvUZ65R1)ak1plmy&oQIS0 z87Dni(`A~j(3H;({VO%ypedgfx^wJA%ApVGZcRU;>7AN>QPYPseMZxlNO7xP)-(sz zOn#821*DMsy!MxIHmbfC<816tS+}`HYnJvn&Ym-4PEt(I`=(GV&Kdk9|PyjB!U0G3>o-@ufb8@4g=>& zjCWiy7~WDijdjV;@L6{+$igm2tJ45C9MmS= z>1DfQSc|6H?Ux*Dh2tqTJ~G}R4{ws#&KW{E|!BmW%f9g-wM6}cVmpaC2+9i z#n1&aKII*3oXWjYk_cmrslW5!V9P6nyh6B99^Fm;vd5|Qkk{3?jh(zLFKOj3ZpqsP zyD1Z_&b3+jGk)3Q)X7%<4mM7`7xK9NYz(cuvd5|1Bc(Uv)M2QIuF5+MGELiKT4e~= zn{jGNUXMMGQ&Xr+jysI8ie#LG+U|#9oXWb7jGa2`!)^iOdFLb3pK&VNBn|rjGfoYA zaEwyTTj}p*jQUR~2gjJ7CPiEQoTiUy+LtkBbIsOpx7?n_oU_VPy*?YeoE-|ffN~+~ zTPE)btnJRgcQnb%_BqM6S!(RLdas+@`4rDchFeD=&M3yWLwlWeoKZXH{+Bjytm*4J;OtyD03}xQ;{+;dHC%)M6q13{C-lQix9DGM~V);&lZQEO@&m(?! z*OA?#-mD~Ne$Eol91G{iv1qLpmxn})%N!*am__HIkvlrtp;(RPn)a~Bbqqoef1g8F z=W;w(Xd5tUXnmR3)t}4n9?a0XfkATo#UL13f0kUHA#Z@gDEe#4@&AEfXkCcNaD19v zxF)>?90t?>#gKu|B*C2n4nyk%Q@CGl*`kca+&4ny|kYvE7DHhhsd1o zwt>WIn5g`X50|J}B(+36T8+yGSJP#*1X~&3fI)ilTDq)>bHJ|btMkhtim&Pvt|70{ zznFP(S2+1+z>9C~m7&+s+N+=*PEELm{u}<>>2$_T2#WDPYyBQHf{^x)32;mnOQ z1+3@14ch=s3pQ15-Y`Jx-u)Z@G6Q9$rDh49i|0VM(SLNBScEI*cwECtMzNdv(J`~8 zPCJ8#;LVs;HUngOO6@@1X)T?7m@&N18wz4KxVt^U^F8sn0`>bMY?6gqaTmd2U0)vEKMSW%Z{@b&#&3y-E7@1Rv!O~KrcL%zh$lc_pw$NhJ> z%--dhS9UWtjhpivd$jZlhDsb7^4Y<|5stqnX8-}Hsii`1k3dfGq#DP`h z{SPhL7{3MJjh$Y1f#FkYjIo{o2fG~YD91c_vK$iely-&Rn0-jc0 zcWDYDrWiM&t9j17@Wm}?(!rc_5pJAeK#l0^dCnGu&O_^6JLik>^s7FkA7ie+LKNTl9{f>CTQVX^i+f$13@g!-@|%6+cf7wY>T9v zKa)PC>C2k_Cn*|Nho-;R?&EM<=srQyqe&6}Sj~Sx(*@eSLh~y%Jy*N)IbuA12Ss`8 z8@Qi)kL&(CmMU8I@A15nna9p4Pm1Yz-9KM7$#4!v{hDC@^!+lg!W_$UpYHJ79sAte z9sAmbVy@)qeHwimOYjkqJ}$$=TBJq>UG zSqXyK&zH#M8M2>&V?MhoIKByj*$;spI6hA<&;I}jfQ1`K0-ucxv!A~xt_lRRA3=oF z?B@=+=-JP+4C5!<-1-)Lo?Rp}9(-t+yNT=w%?3{1FD2X&ZtU~0VCu#EvM+ZKw~cfV zXI}mhE(H#jcouTOV!Ic_iCi#ZZpF>PnKuXdoEZsxF@&8OTCG#S{@oqV)N)tc9IU*V z9`fuskHR)UGnfI^-7$|&_tOu#_gZmhjGJ1z%L|~o!vfoSJA77+VU1xA)7^|o-C@;; zP6qqG;MB7Ui|Sl;3lpAyjBHF(W%K41Ie=zipHxiNZ-7oq1Z!l#7LhYsuYFRq_|Q?* zc9g1%t+%6%?6$mhdM-tIT*_pz9fyn4vvn2hr1Zsc(; zV9Q$zd0ovJHtKjFVlK1t2Wu#A7won??#+;t80ydXZ3k=TZ?}~{Odmpd**ZL*guJyz z2*T0I>yw&-i1`Z&$ajh{Cd%jF01@GwVK#17Z{`du5RPLzV~jjf)eS43l`Iq-*{2_D z&ae{g

o;{*64#sgrxpa|SynEGJ58nV#nmc|FV->^nyJyz`Oi4>n@v3=<(n%@Lw5 z7ITE~h4=S1ANT>v!}0h}HT{*Q>?7#zx=^Ljg;Z&#g5#&~{vIre{>y=$N6?g8E*dOY})rp%-i| z*r&f2y6LQkXl(loxLa|F_*z)H9@{dKF^6CsJsi;Lk!$~kje=NMRhKcE)&-W7GCaCa zTJQI8_wX>a@59u8RQ;X#czy4|Gt|=I3y= zj7t5T-i%R4TKPNJ81=)DmsCZRe^cJ2Dv-@(SH+!>A^M;Svauk11E%NU;ZW{jGT z_ik^-sK+2&Z^o#TdDD=gp2nz?(OwRZQHOcdP7BL4(e)ggqK-UP3O<*F=vs ziLk*jbFBGs$_U4r9KH5ttjUqBJH{;rp^3(tM_OaevDd=u?_;dVcGdk@bNo9UYeM{c zW~^D)BDu*Pa%PMPdo@aYr-M{g=ZD9dt?ScGtyK+`o1ph?9iVQGMAs+T49F=ohsTzX|GA_YBg zo<;9K2i&dD=QSkT_KKc3xQHz0w*a*l&`&nax%OLt7`u!}P6IJ-Wj^}i+D{I2C- z2(IKf6&(lOep~DlQf<&9-?9G9x85}NtA_Fi=Q{g(Xt3P)p zs2R?FfkATo8$qC{@Hn|VL;ea*D1T8t&Eo^s@xdPa{P;>(<2ZB zoJ@>!1D8sC?x;||nptr#$YBoM5i*KhnTsX>hNE2fEbLMQ>N%_VKY&Z7TFxGMfmhZ1E!a2=mlV1m zgWC6@@9UlKY$Rw3FN4m`BhSdWTQvZ^YTDmDI&|IiaU^=^*E;_(RDQ>!HR3@OmS!q0hVLmH(=c&hb@t%8 zfEI0x-vaQ)POq_G__P^g{7!&_T@J1j?tvT2!S|{u$8y+B99W%K!CX#@HpcIC@V2~H zAdlsuJW?aC3U(6*R%aDDglV*BW9oSgcw1gOO_Y0KhgXyFN+O|ghOxqB>-(O7aF28mj&hNu&}_iXnh(bWTsRJ6Iau#3t7)UK zI`_cg1GH#k@@IHMZTFtBdpiZD$J8V9_n_}>dN{mtl^~7YP)AGlY3WAhMhXZ6{N$Jt`FUtwEJbG04?~e zzn9l*S&v+&mGxRQ)PE)q`f2T6r+M}Zz0?iBHrSzRK#%LSvu5}H?6}LQq=ZMNT*zb` z6*@x3(0ktBw)gFnzi&IfjXc6x(II2%cKvOobHy`WvGXKu&2w*$!1t8!_grV6mpEx( zL6T$b-T#8F<&iUIVN6}|BF5F@_7-4V&2hF@T(Hk+-%Z`h!4A$`Gvja%{`R=Lw`1r2 z?cetfzq^F)Yotv~yo+5Mn<;wAC$_Q7fF9L^wJhfObtUm<=+&OTB z0`fVGrxcKHV5Eb5e)$^w!_O}{V=$jzeujyDobsX_0$C(`hrl)J^UF~Zc4fRSrFRC* zztGs+75R0Jo4l0~R}A6DmPuIIUC0NTv*}SVQ9G#*E$2N67Twi41g250lf<=f8yq?4 zSB@!hEZ}Tp9WD`hjEJPuT3i|5ZaTw)R=5ps#Ch0m(ZdiGIL?j}i|8oZfq9MqG6OTQ z8DQ1t+O+f}ofhcB4=<&tYh%)<@D(@=lI)7=bSQa+Uzoa z@RF|2`>^LzyxEvftu*r~`lhYkpo(B*fVcDpxEL-ssw6~-7bwDSZmKDP`T%m_NL#LwVWrxQ{}>7%hv$ zAAYv}S&6;NzP!omi1GlQlc-@L{W{yEO6At6YjQQEu5$;(N$qL8t>b;;m zxesL$j?{7CeL;4+obQ#Ra*I_I`8VlWPMzEj#e9!CO_9mW)w}V1XOtG}yGYJfFm9|r z_A4~IaTz@sJxs8tb1kh^(aX9XVaFgcpVuDeVrZXZ84h%crYTM5YRW!={wHg?Ow$#b z^1BH7S8BRJQ$AaCzgSbQ^N?p*NI#+}*EGoAtLcN9KBei)n*Jv#8e4~^zt`?u|DZed z!$^9xZ6Ul0Y;qi=&?dK71k=U{(sItD>4TyxIF zpZ^N}qM1@XILwXy0D_qkx26eaO1bWD;pz@@cYx3^SMCLQzaojP<$pyCpH2?m0juLQ z;~G%#L(dUR8^{DdhPX@6K3wFt6YgPG9_>d;#_;Dnjq6ri8no z{3>j=^Ef}lr3;nr)`r%Wnsusb`Z#hv^yB4!h<#1?eq#G&pGU)apxyXX_tg2@!}$zs z8OoqJ8W+cf7^8@puc;v>+%z~CJN@S4+DMBw#*gnVW2e^_aq$T<#`qlv2YcN1GBU9T zZdlSh=(mTB5Wx4FF-G1JIN0)T0mE`p9+&l4FXTC%HF02dUI&w;MH^Fpe8<}I?f}F0 z3+2(>$m2WLmKQ^hY{n4q3jJ7Dw!HhmnEb`zXXNd&;=t;ZKwgp-ZA@C(x;773`NJzS zl=m^%8HZyGnuU<}J|hG?t-L;|DTtUlG>ERov!8=6yqa_{=Poe48PD?U9gZ`MFX*?zMQ}B)xw~?&^+QxaGiYb@Il2`Oe2) z$D<+pHk!RhjQpUTdpJJbF}ta~V|(yo$F$?FZXcSvrz1D1aE7-}T)6M;Njo|`?(O)# zozTg-X1_Nd`9gl$p_?NGkiUwB{lXdsWZ#mmafs}lOEL$<+~v_aX9#$MfgY@V9tlbm zhL}U#CG;@djK7N@p#a;gU>it64I~G_`dM8^e3V@IzEvy8vyB6KSP24+ zqWj6^8S*9r$NX<8M-DqNwkDtt$A5)mwD-yF;4p;#HA4peFa!Zt1rE@I?5d-nhdUWb zK@ZGOpY}ev7ZJML`{W9G=)F%q&&+WW;I5TjQs5=CFSY;4`3+!m&$>A{8~dNo?w&6Z zRt@21D3#aJWz7(76RGU0ht!Amih^c{<%&=e*U;A)w(6Jo*ZZkB&@loBGnB6#cZ2xp zZB&Mhr2A!bZxDBEz_Og%s|*i{;aIefKC7O90QGb_6$?i&f6i<;dCp&98%W)%{*1P( z&9UX-xk>$zvI()X_K)n-_K`nx+*7B{nzC%h$urCR_)h=gon=>)l`URcRyG6Yo1co~ z&Zni&|rP_jH@3e z;rT}y#_)Zy85eKx4qV3E0?Qq^_*60cJ-F_})rRYCT*e&QZDM41%5VbK!mVZwzG2J& z>=c8|FlU!LHCvF@!KYcO(j|8qfRVGBBADVGv^s1Yqc=V~^mDhrxsuwUz&hCR+0R8x zH}Gt%!|Zj)HF!L1GAd=*NtzEwt@N~vVW+~4;d=_l@t=t!V!-5C4#v75D;&=QWiMci zv2y&#alA3yW07b(%fS~bI&{g`a!?ZoR_8@z^jOQmSWkzaEpKgsicfhgpOIGuyNLrU zUewWih&;p5tiff=+W>imaHG7zxQsl$^KE(aAkXd}jb9_e+48np`D6cTnoaqh?#_e zDBG$rCdxx_z^!UB?iV23IKzONaM@VLr3m+I7vUHlO(`_5VWOOZ;}LM74Avj-59=gs z%dqF8T}=cv#-wX_Lv8n-=Z1C)OpmEYrt@5oEjlvX%aZf_$f$Fj_k<31ZYZBEE;0kL zw`C-a@{6PnjT`IH+$-J-HJ1!~{E2cdv(aW-7w&c`dIr=>V`+PwW1^AD920a3!-KNz zk;)tslzjyGlQmtY=?YC{jtT!t&2P|@&l=-htm);N?$-1pn)2CTxF2ZxQ%!%R>7O-a zKgT(!tcZikC>-%b5eWSG+J7x6+_#XT-SKQ`@;7S!7R`TIQzMVFMovsPvSm6_fA}+$ z_k-@hGL)8JFVD1^HLv$)T1h8~$EX}>nYk>_0&twwa)!WtQ`^Eb1kg1M<{a0YH-M+% z6AtF5)LAp<1MqwS*mz+dFoGo`L!2lc2t{Qsj>WY2u8+XM%#``7aq(O3L`(JUM0pvZ$?^U*!zxb3l`>CgP#&c-09pBbbk=ljxw^m}VjIhYltGSRjXvegs|HU~j+$TGCijG~fP|=s*4!Iqp zk~{Z7b_Zndoi#$`{k!mc?zf9C>eqbO-mCMVXXkc3=k5OVT(%gTpKB($)V$M^SJAwS zSUK^46J2W_%=vAhp5H#h<|wya&2PCyurcmyu$eDr>R^vl>)OcW`I>NFS4?HUq#T!0 zWRA~6#^Lxl93#M}860M`$G{Ks+e(lSFbX&?)T(tiGE$am-6!B?PBxf?prL9Vznd|t zb-Uo<^3~uT6MlKaA}Q6nNv#Nq!*?4TcTzE}D+h0M+^N^nx?IlTRrWQ5Igg!oE8SPe zSvo$2d{XoOjc%n+@_&UBr-BAM9!^}8*IeVp_vh_!z8lvNb~5e>^invpayVHWoT1h9 z9e*<-@Kv&`khvUs7FaiF9A^#X9^P)T@NAAlSCgM0{MrSdMu!95lP?4YXofrBN_M*Y z>YbWhshfgMUHP@IOAuQ)dd>|MuhHH}M&4_uC^|Zh% ze0Zk9&hR~jOIdMXbtsP=vN1;d0yr2u{f@%p%qQI#W91mro=2@kIeMdW_cLTvy)KnR zBk%Mqc@JUy$8xb=NKL&|!EWNf>hMg_$+T!=>Uj-#JAZp1k8dK%!#g~bmpu5#|qt}#a5-Eg3t_nD>^ z;W&;qhE`r*(bTx_LxW}eGsZ-D2oAVaO~!q6FXy52dN~ih9TT|cJFtc`QHCxZv^7NIlyN9;vGMCF?Su0 zZ^r)g_S>)iH|CtD1wgglLAnjN-Z5_mSsBeGy;_+MCg5A%&OU{3<%nfGFO z2Oh~V&!`I@-L&M@gPr+h8GP7TZ@tO8$tUuc^E&Pu^4o#?_sjg$o%@|VH}7Yi=d1Gk z5>3?!+NbXyNxydZrEouxcYf>VBV5q_$)7=<3db;EF$oX!$HM3>adJ86@vI+;!)UnD0V*4a?EELgb5KZ z$CMUJFGL5D&~$C$4a$#iv)AHSZ_bjZSj^j624cCyGA;S3Ghkyear zAk43n2+gh?{1*{xqBkRc4)W*CROvWzdE?AdTo7MNzjDRJ;`cM1*^0}L*D?=t6jvDk z82#odZbV$(NAnanGJXm}9;>)9aqgF(R;Rp@q*FKrNxY0J=6Va`chPSVomi)LFy6`J z{E{7R+^6#grP@n&c=1=6;u6JTkAv8;@P6KualBKw13b(9QP(?5)$G}HqTmlx@Hr|& zL3}$CJ@+c{i^Z82?>ttJUviC$3Z&fFZTPz#msH?*XP7%Qpwp1=;IElA?A652WQJ<# z!Zf&jkhfl^0WoY)em*@LXTdt;2>AaM*J|%`1?eWo`@D`=kZyLodvv^l^cKhaf?{Iv zUs2?}jOdl*$NzvVdS6srK|#8z*75H9lq6ml{~%KEzNF%fj4x)!9$?*gC1c`OGR%XE zL`pLL7+t^7DBQ&OD$<8<6mD|-9L9U(v%*a)NaF(y?^`N`6gcGhF_q_e;E?IZReHzA zf6Ew8eo^Ahk0+VWr@krN!h&=?qDktDlrI#~&F5CZZeL?IRr!HcnsVZVVE`8}J*K%8ay5!{@>dm-@& z-iEmzrJ}hc9)r7gymzS-H%Od8U&F={+j%Dpo1fUtz$fUqg^3|9*b}{5@J9AZMkJnP z;FGj(D)9^hFYs7gucRFB2GB*?cV6Om419`rzr;O0v4)`*d#VeXpV-B~OLX8xiF@d~ zRQsNqXl1&~yay%jvcyQTXXwBciIXU~!s9)`4NN>v-)65_0L?DmnWlZ{*+>u(op~ zBj2jBCt0|ZlPBaBxGDY*j2S3gD|EuG$_UY5s%`J zdxaEu6Wqi+DUC}vig}r0CTcb}@h~$r$$egY^PLziyrkFgoS>9dl6T~r81lX3ZSWG! z&i6Pf^GYfb zzhmIl-W8IQRS6Ecy$d{ zT&|foUX`Tmm$bQwN0^gpiSC!&?IuoPzSn8~J~z?9@HLu$z)ify-<*n(8P%tGG=+L?w>8gN3}vW13FvZv2Ni8R?AX)8iKL z@kXPe=g@!+aWq@6Yx{xJNG5i3Y9M!@&s5r;{kW104;r=N7bev06oH00>%W*G}SZ9r+ za-e$&gS%&s=X}xKE=+}U)Pt1w37V&jk#5atH34;-QGOFGEk*WzqAE`u0L4GK)klj@b_!_hOA5we< znr{VsRRXS1C4N=JFUJ?2$1z5Sh-T;tiE-5J6t^tcT4nt^JXpJBTT6Em>Ka-$SJu~;Rn;}{@a+1!sHg52sVSuu{w1Zerj^%~ zrONzm@bZV$&X~HSd^(+$;dJgZ0beam`CDtJ`xn=)pT5x_@!J_wOHZ3shcmoW@|&qb zG}Z3%k6g0zOekb7onEn|VyB$6hgSkJVB=eANrKr@PY?E#P#g3uD=0L6e5%&sg3}G| zRo<08-onO@PYAEB;<4#pqx?=a`&2M}F{ld4z}kQqHr>gv;jBMCPvLh+Y@l4nGx69e z8E+>pSYbYocKaJV%xRw6nN}5!ktDm5h+{{QZmz6rseu=t@-+Q6H&WeE!7(`Q^wG-wx#Y81Vt;WtlwO>sYVA?dtF$$f|Wwl_jS}O zPdC@JwAOFNlq%g=Tia3t7hOk^80_iV)`sdd`&jx2=cHw=b4xQ$LXGMzovv~aP?VPD&5c{CTTF49%jY@ihMKLLQ5J0Eu(@$#y1udn(P}o;G_S8|sNUwJt1sS+ z3P3Q)#IiFME=?~#dEr^hS1dmJq@Poi#usrH(Ed?7OpcN54}s6G`1?MQbIYboeTyo zOYVERu_4X+PfNx12h}xwyzc5ttM$Xz#YdrRDAGi#8yh#)p-!tRTkv09-w2|Ly1sJL zrb=nkusLb<87ZFi29z1~B~Kn(qk~4ETf?@RO59ET!RZ#1ly!@$RQ0)uZG~;ldPJ*R zo14+?r>k*`*Ecr9zpf=+y|q%+Tvk0**Vj}wV3x>7gHJf6FQ{8D?QK)#R>{VuDn2u& zMdRr~3rBF{&{Bg>VWkj!n0S}BNv_goEo*F8kAl_Ipe3qDTj#ow55lIV*5(@ZELYWU zRDDJrT0QPQDA|{P>Esg07s3tbmrrD?(_6sUed3>e%_;{JZOXq{Y@&=?d$+Hnx zTVII>33FxkVJhWxi#)&o2a)vw*}$eNEZu&)n0HP#mc9tUZ4=%;e7Evl%5Zo=I-EI@ z$GAky9qQGkIps%YDlUfaDO?I+D9;$m`?zlFRcE!r5r zUEuBfjetCkotQsVQ7G>=*cpfSk7hXWN34PyLqA@3X=>a@9@#ZUelG%`tu&c%6$po0 z7aGcP_Yt^QFDw_!igZKs5EO#13%SfXvz(=D zhCdV-xz*H>tm|+gimUj_xYBkw{xv-PkGFWnpKtN>=NV$ksQ4Tg8=n5_EZ+E#z8e@k z?*$F#3B=UdZy7_sDY)pj78m`w3d}hJFY?EO8pH6X7zUI)pJOwZVEFUtkMeOIK)#Z^ zHVnTBmzht{e=~U2AN|N*0cz$E{o9folX=7cz zcrlb2d#PE4{a)rv7SmgMIEELvi6Z-EjlpwqH}W_JjeS0{M@n7IF;F^eBE|FYx^^!_ zxyFuC?a9MKH;+6jhV6&^spJu#?S=eW@?%u{AkTiaN4PQP2aES0!?|Z z=zoEx8#LXm=|?pEgr@gs`VCE=)bw99eO1%_n*LhT7|PFdxMM!)L`~1rv`SOf7yV^l zAkgbH{|Qa+(ey!0xtPT8KhTtj8hKfz1^u(;GnqH;pjhcQ+(=TcIf~NnW=^L836jI_cVP&yT7gJV7$bbeu1WwNs<06 z&Ck_zsdhh4(;Dqwr)jHp-=X>4ntoinm-6{UIvn$O=@{7X^kJa80v(_>?BAgFigul*4phrvTj$70DcvCG+a#!O zBf_DVLOIlDVOZ*-^te~qVf~T;)h~(mcUxRURgv**4gd;U~C1FZ&OU9Kyo}H z^`t=ZJ>*IhNTg;%1(NeADO4bNkXa8ENSK$tDUcB4q5_HFmZ1U(hPj;;NFHOtkpc-Z z_C6?(%x2a?1(FZ49=lN>`79F;6-dS~UZ_Cw|5)5mf#kE~LIsj9Gk>81$@dwr9||OI zGWAe_WIOZOjRMKPGrdrOq?+l43M8T+6Dp8g!F)yvBqJEHX9bcUBiMUOfn+CgBnl+| z$`V8hB3M7k}Zlpl+WhN9Uko=T^BL$LI7&uZOIiGB#Kr)AcBLxyZnYIE+4MkfD zB=szWr9dK6BTIo~5T$okASq*UECrIYAVw5OqyvZ)NJKj+QXnZ{w1X&+d>H83R$E;g4eCBK=LHnNP(n^qAUfHBbg6NfuxB3mIBFTOv_Rr`8vf}3M3op zZz+&8(BD!ZkuTE^ra*E7BJ@Rpmd;$fw61!opR8S$!j#H&`3!ca!@xUZ z>mqO-NJDoHlc8U*h@bFE$4?LEleo9PiUF(F>vwEQ(W)LpV7bf;q{)im-8Uv}BWany zp2ER2$ZP@NegDum0++LJw`}ZC;Ar{Fge5~A0>S#SAoWaSka#2EO8QtR^|{aS@3UBj?j7DE71xudKr`Cm=Hym8X_95bO|yPTiA1Gl^FxjXDI#{WWbao z(jv&vR!~|aM#n=bTYzJtk#-OomDW@VwL+8q!;lFg8DM z-?`iG1dJcV0IJErWb5$~;(LhiCA|DYOm>pyF>Vrbho*)wEKMhDUO9*VCR_?G#!gSl z<7Yv}80!K!7(4ySFi7DW(HP@*0vs%7XEzGL*%HgaHBOcnFHt8On|~f3cX7GF7-J<) zWXrn`FImQ?JW?aC3U(6*Rwo85*Ohxd=>cF23w z6cRlBc-^Hbh?teY@aGu@lwoM@#f3K2BnGp)ujIvH5!u?vXH5B#+_IL_+g2 zu*J1-BsNbO%qq*pvYNIGt5c5lGM5%@OuB|Q)OJ4<*gTr2Wz;@u%R(1d_F1d2_|OW= zyMd*%4r!Psv1vfNp<%sdW9#fU7?yns!6!{& z40raSjK5n`!HdBQUJQDVb{D)D{F9m&ycoRT#i0AO`>!<>ycq6+7lX>82&mx2pn?~J z3SJB)cW#N+$TLnkZ!0{E2Xr9S{~ z@KHlef5N#Frft2(N#`8fhkY2(ufb&Cq%r*+eVe1sUSfd5-^}AV@AEhFU4Gs^rqk!` z#cU|7WsOd?`Hbdp0oFGMH*NWT!B{1p8%(Mf*0D(K`^`i1D^87yV=dAopR5juGl zrS|Z7yAKJ1PD;&&=;ZMf9HNtFG0_m6WM2A)PFCWc5}o`DV}|IYeBGV>dHbtODncg- zDf9uI{1metqLa^1RyXM6No-~zI{ABMEJP=-BNw8RcQRy%P6~J)qLV&rxF6^wKmLx; z$^T&r-Jp}D3>l)6moa3BPJWN&3(?7sk&DpD4Gh{dIyo36cyFPT*Pz6LPJWEHAwnlF zVoezQF-XYx&akn>RNe{0<|pPdaD-0&l$8;olbabhLMOK}aD+~BR%)S>Z44Ztld_9O zgibayaD+|@gc_lfOLJR#${h-p+UtIw`Y{ zgP@bIvJe(JDQX)LI{AH;DMBZAPS*3EYdF#@9D z$j7~4Xiy9~W&*Poeq243CHy~t5^|*f4p72;cfA(?7(@lK7qqcK^2V^DvFxUYos&@g zf5a?DlXW6Mtb-oT-PaW*|uqOWfdsC^cT9p*#73{EDgv!{9*7shS!>Kbony7`~@)TvIb~V3oaRFR&bpbwO6R&%%Rm zL1Rp~Y^?8fl%o)tXPH?CrX0&*H*qNAIRI^CmV>dL4nHdy=dICEy|8>nUKQ*n4y?|f zv2K{8MH_~r$;SG4NG{h6nLlJ9l$VY5jlp0b34dcuTG?1%A>=V%l*jRd$=}tmn>esK z)V)vAqK)y}1>P?2NGpF>LJj3*V|}Y3?_pC&grhuOcWDYDCIOIWo?$>uy7$74w$fz0 zPD8lfV0{}A?hzBol1I51p@-&n;6F(?66<4qGpnR5t7)UKIuD_}98HThCVz%E)OP<{ zVSUWMGInlQ9=4>&48)m9sAe&2SH$YrUl=3#FpJ(HRu>IjteqVc<_2N1X5JGP2mDyD zIM69b%mXdg^!=K$U!XgmQ&Pd=KsRW9x2A%{!ChvTTz`v?_!Q#OG zTJwU%ffpP{HCr1&ad}EDlt#I8edjKn05f-H%(!@(30O zDp(w-U~!;=#eoVI2P#+`s93(+1D_B3qC@xt!~7AXbNWQ>088^^HQnaev8O^3lm~&!=UAIP&{p7xb1Xce=e9~ zHkZydn@i_$YqVk zd3o$K<)bZcNURQ!D*4zt#~b<(EKUypLs72d|A^i}9#?{8DB=|{)(CddG3B7TRsod5^bkoorG zR|Gu4sFpicY^!zFW$-}AY;QCK8asSCUCWI0V^6aFta+HbQD${K+{4V|-_keFSg(lI zG_uvu#fOf1vfnvchD%pV9O-pZ17`K9Ua?^`(E_%WnN4O-}<_S8u&G;Xi=D^)~bfeO*NsEftf|KWN3{Ub7-)kp&v~#F51m}f@4+Nm~fn5 z;C0wzxM>Ju;!qCdT}q2K#*fdHvD51-xcGz_W30!)0k7N8yana}xS_Z(6c6WiOJFx~ zV0F0H{M)o>WBm9Up*&ELgZZO7 z=WOH&05VPr7tJG($B#&jp_SL=ni}`9C}dZ2&#MpsBAQINvk;DJ>Bbn*Y}N=Dnu`#Q z^I&64I8sw5^y5au!~hFnH*;v(SteWF9yrW~n=wY7;SIIjd!BpRIbnKsisV_Vk?DMX zFO;z?!}EJthRB$^tX%wzJ4pHQC@5^5=b}Tx^LJS%!ytbgDD9L?ehMhZgUl)U%oox` zYz`Q9!|2%}#5=`S_q$cX8W)$~|RWvgyfP>trBN%4@}q!Kf?!m9OgXw1Rc(JcyKj-3uo6ki-XPq4LTlggtKv}mPEd2DefJ&7i|h@u9ad`Jxd_^g_T8NjX!Px3 z@rw|_aQX4c7T0_2yE_q!+IROb&ohf9=T7{$OlOwjg80ADHPpU)oY@Pt?|#n854GGjb?hG_8l0<(!LwPVcyYDf*aBmPf zx}qEHyI(N9?-nzB$+Gx~Oz%0xRm6WvF4Vp|hV3iTz7vJhp0)3|)6IKJ`|k6|k!auj z2a6tQ-|={xP`@rnthU)$Vhigc(!RTtaU<=!V_1KY_MPbFMcQ{)GH|4Q_ZBNM+8g9n z`bOG!vJXV0eK(!yM%s5*F`-ENPP8*4?YqYrIMTkGOE%KJo5XY@?K?i1w)WizDB9A# zlVgW0?YlqI-_pL@OTM%A-AWe6(!S%7I--3i9YCag_cLZL(!LYjyo2lwG6AWH_T8T- zKhnPA=2DjS-B{kWgJ|FVim6)KcZ(T5(!R?h8)@GSrtnDnPIhLBwC}E_tVsLryNnxY z-~EKXk@lS|+;_V-h`=O~_MIq#TH1Gir7TPP?ieOzY2U4)zomWm5#Ab0`|gAEx3uq0 zp}(blcNY1BY2Vc%Kwq@)s7Y^W-`&Ra`=fowXVqxm-NHh@OWJq-wa9SpRGtly9|Vu1 zv~I4?y>#+s@(3EA)5P4mpTYa6Ef@~D%i!fUxy5j%S&Pf;?@==u5!{L~Tnlk)*b}&G z#?)@Yjv+OC6#ScBcgBvB9Yii-bhl!BWs~DJ`w%;>)^S_jLL%NxNa%-5=t0K6NfKfr z9DDJQ70SLwWUo}RuTiq!FtR^p$-ddhzQxE+%3)Zc>fpn?JO&>nu{xM%v?N=yBMQh(c$GjXWPR2<8aDo<^|F)x0>62Ezg@++MU|7oe&2r*3s zz~G&Vj9l!u^%#?stIb+aY+|ygpzzd!rWu!k|91H(dqtu86W6E`zgAajtdPmIEz*^?nU{o~@w zv^t(cDN^CQjDL_VO)QWe+I(^xLO0u+7{$6?Z)?KCUu44$>Il5|cQ`SdkVHG3*n5h@Of$`Wc3-byg7K9f~ zXzs@*2VBD9GAQFS90p9C^dz|eWgEjzg(JgbTq&lza4GnjI1I>eH(CzHx*#ju&3Iw* zZE1`N$2Dxr*?AV_NFnnq2gZwGIkNR6Tk&%LmJxz*#(H{|ytlB3&T>&6si~JL*i9T* zopP*GucAd8Q_tC05J7cUK#w2F=;iz!7gtS@=X3XUNQN*8g>(h zGQJ6UNm{fqe!IZi@+L!GA>5chLDQl2R^=J{|;fMt1@RZ^DKv{6`{ zCbXCNv}j|}HN2s=d(T*vodVOdDhdKSV^eIYks&t4dl?y=1P$i-(810P<*_A2rZYCh zI~f^YYQpAO%aQS+897|DYeA_eY2O>xBcFHr@dnbr9Ml;4heOzC(-YI<^PG;0Yoh_L zanCq9Yd%DbLALJGlx620D6A)?z1xsB(-cg)sco-|vnnM0LZpw2=`YGkI}c%)c9TwfGqLM~q}^s=;Y{~1J`Fwc zKJWD+nz+Zs-yc6Ze{s<;cepzr&3a4@RC(#RxQMMB8bbz&COfXQ8&Equ#lk|di)|Ep zLMIeE33+%znIJ%c(;A2qiC*hEnR}8O!AtMxe;5=54Uvdow#=)_W5xWh4x8sru9PbQshX!;S@*Vy` zS}af~c94N$2iYh#F~cB2vEK)Fb?~`@bdwW&o}$o_3NU^R?orhm6r{H}!50)0ixb5T zLKK^jS&)HZ2lstSWE93PXFY~{$o!66h+@BtFhPi7FC-VD*hD;o3=})aK(T{wsT5KL zX&inVJf`wI4;(W6xXS18@$WIc5XI&p13?Cg9ei6!SrosY>1CkUK?aH)gedk*=JWec zK(be|$_f6+g7%DJe-ZNETPXH=v8OX| zgktl_3|hS%Ab!cSUg86+77N8*zzWDhv9XKSaTq}PCG9zhy$s(O#U8=pSSa>Xh{+G~ z+=O(knJ9J;q1bXfQcoy$Fu_gmd#xZsvFEWA(H=5Xnh1J{Vh0<%1i!cqA{6^YM$bgC zg9ycLVBky?JBU#1AJI1x#SS7AyOfeMQS2bvLxz*jAVRU{QB)R+9atzfH`EETQ0yQJ z#SXGi>>vxp4zf_}V4zU!;36-GN zopJX;f=iVW_roHB(RYi8u>wzTCnD`GA1lzeF#b(%IOCH7eP1h5DvAkxe}wRK66suL z;OTJ*4A$KLrtGhXY|7<;&Ani36QS?V2*!SnO6W6^(3vVBVf>p8n1t@P68fr1=xZjS z3BZmC~bdiBz!{@_x7OX>8~bbnAOupS9{_sc6 zm{K}>TG`UFE_^Xz02t2oHX92kZwy;#QZuw)hRfjIS8Ck*RN&qw3|rk+2?P9m#0~z{ zfeOViQ{aw*z)VD2<+ljMu0Sr1*K3eE{-d zWW2Q=H6qBkXQ3ztG%6Nh3L?q4hiV_lFHZa?0Kd~+IO!kqvIyg)?RJZ&hvmGH_)PuspmD|ZF!3zk99zK zERK=K1wdO~Ie^jWa5KjEW#i1S;Zci#NmvZMW9gT5GLswRNrQ zqs3>fRjXZh*S4-ph_qJi)?MAITmSX{`+ev8&Y6433j}H{12^A%^Z4d*=FFLy-^@4b zVR!R)KI9Q2VE#~~NM1fpei7ttvO*#p4$ur)l>%J>+ z^0X$*HkUvtS%%p7IL8-`M?<&Je?t@?^nb_UBP%RB16Rj(19?91AC<>X z%H#RIGyiMwV|*JPSHz!+k#fg~4QnuGS_f5#}?2 zN?b>ZhI1R;;r}uz{8@K&XCKlhE{_5VE)P1I;X$jE3N8Ov3#^X$n=yc>5?FsZ{hJ!M$Gj7Nr*`6+KAs7d=Rz zx2gL*N*`7FOQl4;!(Srp38_J6wB=DjK z2~_kTfeJ1UDteGWMGq3F=s^M%JxHLU2MM$mFFVtf;}oEx2MJX4Ac2Y=Bv8?V1S)!v zKt&G{sOUig-8UJ^5j{xYMGq3F=s^M%JxHK$YdFz^1YYzYfr=g^P|E)ObtkU&Kb5~%1w0u?<-peZz9rXzZgKt&G{ zsOUigou&Sw2MN6BK>`&$NT8wz2~_kTfr=g^P|?!YDzAuo6u10Wwe5QmSsr)GAXDL5ddFhAn$0$TeJg7TqgXRr8I^HLaKVchi z{B=wWRl>fEJ$M;<<}5IDFH^hOCpM2W-CpM6&cwLKy1stY`pyfh7H=LnY5iur)tduv zU1!HY;_J1Wv)+TR5c}^H2ak_t=jv#3apOYO$5e3qped+N(bkks0}jT^L|siphT(^= zyq@|NWOVUJboVHc98DDl_}_gqQC+E5aw<#znnfWQ}Of9Os|3FaYihxzAkyv zb;;u8aM=3_`YqUVrP)g;maTW-FbWyOlnWPLmpu78^qk%SEQR+M*mlT@>Z(dnz_Wc* zR$ItiRkwIK_1SEXlrRI86&0o|in=l6g_RZ2XuT7SXz3ChAzBqKiQ>sgv%-@YKW6UO z%E~$G6QL6F{PjxqBWCy*aCYYse?ubN4NAxMo`FTHD*U zl>vL;pH6e)OG+6vW8uEwWWn^PRA4o9+{H}$Lj-emTA^5Qu>MPtBgt0iV%4GGIw5TyZu0fccUem$wtutfR!{LD8KxAIT zcZTx}d7hvw2lK@_g2jQ=;~9a290#+`hMz0%c}yA^pYkwFisW$(!s5UxW8YDd0&8?akBW)3_->hnGo=_@^_N8Zj(Po>0G4?l=7XSf1T3hO8Jh^{T!vAQMy6t z=aha)Df<rBt90kr zt*NeQ8|Dv_sxo3~2V8i}++(3a6_P^ZV~!AWp4ZOA(44D;g!6~ww6R-c{=m6IR_71? zqg`JgxUTcUWbNjGh3htR-cb5?GiP9nqhV!XOpV9FVGb7CW3XtjwP?v|d5KYMC>LJ)SfC3w_#N z#oxjO*CjD+fQ?#dKl*b9zNGz|GrZy1q@gmLG@x6^+2i1TL--hGmLxZz4!fN*AX)f? z-}mrK5q}S?<8(P<9pwl%oKAW9*_JpzHMteu^o-^dk8Qk`2t(e^r+rEu@mas89 zhJG~pbB3P6jX_w8!*~qWpE<)MMC#9+;aQ9UIVRwk9K)Mv%$q-FxC}3A#>t3TXCsa~ zB6|YEvHr{%ZbJq2XU^~}VZZO%}PjQ3~GupAFziX{qa>D~%Eir8Xa9SGN-Im3?;u0L~z2UxjCjD2}; zbA|`dUfllP@@M%n>bifF=M3xvqB%p@$&Tclf!{e98>9aDz1%%#i02AYFZjdvI-E1C zT+**z#Q2k;!9w~_vfx(YKLo#uyka)8-ea88f71jCwaiKlYM$d#<{r5R4jZA;Bj_$FEg4|K?xCM%oeT;9Uo(?g5WoybA$KMSU1+WEVDp$2 z|IC85B*)0WK3v-`M1z4#Io(h3Jo+cRPx9}M*g381{Fu##EmH={(IWcKuvK_j>hy;3 z7cNVT#HQc;@i6DeW|)5{zq;DLG;s;EUpZsqhv9Ptev*O&=l4q=gYf+0K8kJMQW?sQ z7tEstW{2TFN&kB9fTj>fA-Hb9EZ5= zkK-;HuAjSOz6{0$S-5dr!1y*sJ6L)2kz!s^YDD@PFo)qs*?om$8WsmukMh`Um@&WU za4xuv;8hy&}A%Tz@lTejGEq@=A5ANqH=v z$*YIm;=t+wUh_E4H^Xo=XW{3{8=_-P%0pHod2O(}^6rGZ?#99#GrRIeIr(EBX8F4; zPu^3Imw}rZOKUwGTzOe1e-a9HcC(wNY5Tm*K!QV>6bn z@kU+uzQ@9D3QUiw$7bvGa(i3DFw}{I$KV)W*0*nqT-@AHK3h_3c7#qG*1H+jTSl2X zK{;0bnDQj_ADhQl^+~h6DWzCYst6Cb))-R~sE7ag&>)y(G?&Ayh%TxZ) zy~DOQ6wD&>7y>R=_sdD)emyB(EY@b9`|~i-<5CFqY>ZZ#RVwTJaA)5_|6`QSRk}c_ ztnLtGjlAAJ0VKFt2Ph9xZ9t`J0HB?2X`V&2klqA60C#0h3os?lN_7lIo$Vt)uyh2 zlV)sQFtwvI$RszFWWLae=QSX>^U+SPqoXUG?dTkkVf@d&Qdo+yycdrD@$6rn02#nK ze_c2Y?g*FIE4i7vMrsjuF!X@Dabh^eADo%H*~@O~nlPSe4BLc9w0{(5ToUsKE&eZVAX>*#qPw0e8#i5ai=U z`1f}{;9gfzutWAf`Gfki`zd$A;p~25qs0Cvf6&RO?!5g^rF0K>M(q{vj6$bj`=th@ zeMTER&`V>>6nf}*SJhC+-6yLPmRHmb)^W}k~p{%O(tiA4`#kp(h)!nUYWu$4-N3=1T}>3cS2J^%D8 znBRqaHjXp8a(C;(icj|S?)8JQ1W#_lS?_T`&T0z;(OtJJ2u+p86XW$h4 z@z0ic3fSyTcg6{2wz~Q8N~jqHeS!)+mjb=gqu}@=EHC31N?hwoEd3<>>Gu}>r4;AU zpIWg??|>iO2P0QuE|*9yxv7Hg?=g<#a;YnGZ@M#1C~sLPFNKBIS&Sfw)JTN+*CkOh z1WxjSl{I6hm_P47AsvBf-Z*L>dg%3o9M9~aDesQBeh~ZP!p&{?X3K}AXNU=P5zVOo z9T3w6;}31zFy@8I6PB$C23=ekv|K!IPBo9z`HkFQ7W?H-_8WPkUA*bDV|UJN-;6$Nh`4!CTTnOq@3sd^lZVXP!osb8%Nv(1 z|0_UYfA!UZov@(}tvY}0pbBno!?xEcf9>D_OcdF%mEp(thT+KPuN|CEPsbR07~B}% zW=%bX<2S(KPz=NIo;5ROoel@H(~lQPu$M4ne)($$kD?sxIam%3Gpro>YX>}s@xBQI zHF+n%9uAj7&qqt+dSUrYUcD2CA=e=>c1UKdp7YlZo`O95m;4O?HF+`+u*jhILLQ%y z8OvY(+QExX{xBwwIcO~|V?2Gr!;qV}x% zEx;h2XOYa%%HQ{un)@TDT(|x#3OjDJsTK>z`>gx3#;_j>^@K$NHF=~?GxE;IgdqdR zec+CwOL<9IC)So>^}dSA;JanU9_h3~WQOaZ_jhbA5NjnnIiwUNv^M|&=_ki@5N?%g?n$q`_7NPu{ z@7un;;p7poo)jQI$;V)D7uW7h%D1V>&|5)iG>MrN?5dYuQ{Wt0^=l0XR+G+^$HdluDW2VpblJOJAjqfj3d4AY= zN9!d#A4QY$uEAQtwSp~*gZz2t!v3xS*lf8VoE{enMktyq-SbODxl{I3Jheu08}UQw zVZs?TtI3RiJZv_jz72NFsILaWjGDbDX4FdF28S8-W1(XKTSh^^6FRu4KDiXG1)PWn z|BgSHT#pCAg=?6Ut4EA~;?IFn?VXoljaRW3aLFov1! zKm(hbmStq5pEE!BfP-|t4~u)5Uqn-j?Y{SXb<^s$h9%p2MrPLofgc4BkYhi*W76-j zrA@)lZFLw1i7CflhO@Csb?6-Zr60{9_%Tjj;l?10#evnMycM*lF+YyG%}%fT@#C9g z#{3S41IJQkUO^`A1%pMyUiZ0O2mz}t5~#_W4ZAC^0}RVWc^GO&<5iBkEe@>STVOJ@ zs0q7x&*S>PEAKWi{Kilo-5HMZIqr7l@%w3G9e724tSeXEyZd3{)hUe$mw#-DAB|DkbnLwW8KB+r%*o9#I(Gf<E;Ev!2Y3-NJj;y=n6|Q{TM5apvZWp?f~LvBRexd+M7v-Sc)Ix_cM8>9dE@aqKa> zu?+aqAl$cJW_O?`fp2mDn%(D4*}Tu)n|9J>%B_YTeWuCuc)vSvxh>ErvBmCq$H~G_ zr#`SP>eTNkz5M&#S;Ah&vMz|V`_A?K%Yc#{f|u)np48J*tG%O{o?7i4CHKSFuAMvA ziO6N#(SEsNZ_svdRPj{4#Q}ogMSuy1|6I~GRXmDdNui`GGiUct^pdb)x=jFmhBf=qdRFe3dl*!T+ee+PELWco)CXk0425fO|l zPKzcERczn?E7&<-<`wK*mx`}oAHXb3C=bh#eu=ayANXKaLL4oj8{4u5&wW>`o4mF3?s(M>zjSBeJHR03xvxR z>{7Fl^7QzGq`Y2MOmdXhCvue6 zCvue6Cw>xE=+v~V6h+GGZ)HAnl-DOB<@Itg_Rqq2bJJI|d{2g4b^37TEK*+oIdacE z#FC7F^7@llh`yEAzlUHSF6H$#5Z`5i74RBpWNQ|1Q~BdA%%Z#mei6RXA6$U&VxC<@LOwBM~dF zKY))YR$hN8e=P4#uUO$n(y(q7r%izux>~#zu zE3ZF@Y^=O~1w+Tm>wiMuSb4oHf5yt|U!ZTSynZ6%#>(q2W%+WI*C%4-^*6IFW99Xq zr6@;veGO$f%Il}o-%(!wEj}7Yc|9j)?iK7`qQ9fOejfcD<@Jlm_olpl6#{I_73@@> z?p(pXh3Rjr^7;gn*YjPqE7>$xkUrAYhb*2u79J0Lm`XK11A4mQ1+=Oz#J4|&iRE$4gQe}!9liAhm$4Nrf z2TDT6xd|N?CiIFWw7^Mdp(S*(B~-qMPc709 zw)X05*!i%%RqaSV6M&~Z)09QsrR=gqEyF9!HNp4nFWWro7d3|kP+Z9^UX)rk$k zdLOs7dl~hlU%UA89(TLMogu=#yC?@t5#|U)KWm~ofcAO59uY6Wm}nRQEnW?qu9?1d z(YH082YpUn%}4&S_U&1i*LSKis&reM=L9WBo#}`C()XwUXhk)PS2nldQhD4&PaUmW zLl`x!Ra0<_L0vnr-loRp`ZgSA!Y%aOY#f5}_hn6WD{FYMNKIYa;`YW>4I#5V*T8Q( z22<1R)$hG2xsNKstAUF1hLw$Nc#v~XoH3_n{xLI7p1>4lZjh`s!OySKORR!6_2VTY-s?{1Ho`{}R` z3Nb8vcVYv;glk@0*X4JR;V|~vYL6Ll0oNFN7#tZM-)J#Ch2#3Q#bH2(<9-S=W}OZP zv(xW;_#X1>X~z7x{_U1y2+A=FnP)k;7H#F254**I)%zzb**)MmnDqqsx$kL&WTyeA-!vt={pN36@0ce#^4 zu0oo;%V4)Sl<_X)WoS`je(S-z@~(wEuH7?#h!V-;-WtZ?^P|}hAV-%K5O)&ya)D!zGAqUv2=|$>bm!hVYw+VJ*T1|aBIwpEj2bgpSEYr!sbifxoj62 zH#d~$ych_uBleQvv58rZzr?UIAuE4bk7MzpUps#ECmv;n{_%(wgR2nt${F^hnC_w<9(1wtq8}c-teAs-N8Lp~Josmn-+ga;Sbw|kZ4c`~^uwclh<; z_7`9!w!J5PyT`V9#k8T>sq_X!Fs?ZL35V-Hw*3RdBDT#vdNH=mTb*NU`&Ify*fuX_ zkFo7HS^1O7rNpJ_o5@WMxxwjwr<4fWK9O96ZQn&Hx!Cr*5F*(2$0#l5V)jIQF?)s) zV{Cf@OWg;y{T^~9*tXPcgl%8R44oQgHksz8!Wi3TUbYR}J{{7CZNJQz5w2s8)CAiuVzy#z`x=%a#IH+x{BG zITy2^L4OC^KArv!w!NHuZ`k%)1lSgAn;MK9Z2Q|xe|xZPzN?09f18EgS=e?33aHpN zXS5Nv%}qkUw!a8B#kOa`#l^O<=D1DR_FqsJ9~|4Bfy(Fu+m?hr2)3Q!Q;V=|4#5(h zw6xvWHm-Qa7 zan7Yp*Z~lNmwy)scXT%FZwZ;+1z_|CL;iK;W-&qGw)g_~zxj*X{}yg*-+4&3^%K|z zvAVUjp{c!Qab0`EvgX#cUQKvHQg+0k6KPzDy%N|>)Vvxy1(vN`9dZQHS5B&`3wJwU z8$`|0)lFz4P|~`11$&z&*%P6MEd{u4Y+J(31_)5MGTtb$`-b%$2WfY!H>E$A_FMaX zG%)SrZNRj7M8J$$Pk;lKyd$#^Kr1GIvhGcaais9|pcV&KuLS@w*V4=|9L-tyx$+i4 zUIuQ1@govx^71il>VK((zZpv_AJabH$zN}n_M4EGF=3!4Z$0d8{x(1!*X)@;M2Yg3 zk7*AAroG7uiExz1-_1%v#Jt&0O#3h-h@>N9`OC+&k4L!vVA}2d#I)~5yFV&QHLe%O zNvxJ1GP?rP=Ch2AFLx;HN=$oiO`KC*V%F}nVf#1~Kl-(+{{&Do^p61_2hxtlyFj!f zd;A@0T-CC=wV{Uk!*=7@XmG^eu2d?EC*W^Wem9;S){oqGj(U;%&Ov{LK8f`v_nm{v zednOKqCni`zH{&+m6!X@!B1CS?mGuxtGwKI4qom%2bKHILFK-4P`U3MbT^(I*6-g6 zp1p?e5hmpP&bfMxtCa5(&(=wiY3}J0&z{`>v)WHEQ~1mYJXEl zC(lf;#Mx*&FU>R3-o{N`7oP|n$D43=d)m{`dwkWF=-hPn#w|Fb-GwvSTk_9Lm*C!X zo&{foGuht8>$dPbxCdRq#UAtpBTb$Mw=>`u|FNUXJqKPo4QXvcTAPcOw=->)R-0d??FH{~yzt5OT@1 zXsC)nYk^Io1KEFM zW6(OoQuhH`zk^%}Xe~7xf!3EZdl6`T5d}pDvYD4{1FcVnG=kR8F=hl>f0|PBL2Ehi z9D~->4YLiP^?no?f!2#y9X){7pJ7%a(E2TM5omn~3m<{j)qHLdX#F((BGCG2rneoS z_0O4J1X^FueD(lZ|2NZ%K#G zDU;*cF=#!8)f|J?)b#Fv)|WC;3|gltDh92eXW$sLK8kD%TFd$R7_{b_>4Me*ygQ)v zMJ$8^TEEAnodemOrXIRFQD~sq$Z$sz-+~! z^)W0(3|b#d-(Em#e#u7X#G{jjX~?j=^G!&mR-X! zXnhXL*CS~C71m`8TAxc%4ro1y`EWq%@$`2<>ssc-0j)nraSmvGBK;lE`dIoqp!Lb* zdjqYPBEYtQ)~_(b&58L~7E6rq%D%(&w+FQ5yK0~{x1Q}Z(E3-%aN%J*W{p$WKS61M zRL_B%BGue7QP{}C%?Dh7;gEk4^5W0){c^a|)Z%Byu&4X(Iqd1aoR2AfCQH(dS|4VK{zW?Z3mS`#jRy#PlYk0{x~iJ*LGmm>s}w2vgA0 z-3^+;>z=02?55BXrEqqXLS|1sod}!`4_<$YZo9$h?!oB@^g<==&|}w6CFI<7MNopn z)w6l7`moBemD#H7@?hY_LCNf(b#~}iF%kYeoqb-#m@t4Iyw1m`GiWGgYGIjK!gJL) zPi;r5;g-RVrWU^z{BSUPX35~IlN$=w`xhrJ3g(uq3GW_)pXdD%?U9G~Yuch^I=&Lh zNs#UkYMV7MK;P%CKl%r*w*!v0D(2Z_!OxnPE^TW7Xtt`pt+l=R+{JA`;(*i9p%sUh zjmtso!sD(>o7(UIaXeYTJ6vDo)l6?(CI@$y)va1pCw{O2sasXI5}+LPTDPx?&-{i* ze9v9Hynee5Ov^Fh=m4<@`zxQ7{y->r)E{j7iR<0xtkHhk+R=Ph)U2!{0=%@TeQiqv znrGb--oAG7Txb#owA=uV73~d#yt@OXc1w$`o6acEgx-vp;NH&ES9xy+-xbJp&s0^=+(`RA;O&2cG<%c1xRM_A;+%46Ip zh6_=yF_wF-Fv8+gOiWMVvhcMy49IZTI1Xl=o)?bmV*FB?v2aJi!EyGULOHT1B+F5V zAIr;p<)5>@7$5fjoU?upicT4y@<>fyJ?xeTSiNZga8IK}jn(s6;NAQs0q$ksMtQwq z(lHk7FBpn$mZ$2hnjEwaMlb(XcJPks4T#e;nR!R9Rtc}9ztwDR4N{bpx*Lb6@`>w#G z)0VK%@E*{oA~!cTEDu{!Y<9#sYt}nWEq=tLIRrJsdK`scEq*Qd(LW1nhW<8wt%xD% zXn320NQbUD2dt+dI?q?%p_Jv`eZG1(9-YEVOuTttrMvOyus)u~hmQ5~A4*?W`e&sB zpf{cV`zalzbh6T8m7c70iPAQu>y=)o^czZlsPqw~9~6)NAAHD|&v%vD`Rc*wwCSE9 z#W-N3(hcfk?WxoPZ{2~#_|O0w%WFCDw7tNf`=U3Y$BV<+ys?J9qzqjT@f^v#2C z*X{#w!~M2hcfYo=^Pa;tcHaB*jh#O@U}NWfa8GW!@nz8iuGq2%oSL}uUvTd%6Womj zqyP8-SaAA~VqS7P4b8`pVBBUaNN}E}?yYd&)7dQf3>5cWB6e);Lo@Z|4kS+8p@{o! z1}Q4%-@D)k+_x13xG#^&8t&T+Q4a3=34~VM_aylAg!_iEl9Ld`!F_LKi-tU-<+@ zxbLm>%f)?vjw}f7yN-U*VP!eX7avys1wCWj_Y$Vq2kyH!@+i2k)NF+N&SBOg-1lV4 zjc{M)W!rGyIy_V2zH!8u_^I3rs4;eK#V^HsHR3{6x5~Tujyj?t3(wS&aL# zND=NU7mh`^?@bID;lBGb(-H2QU=42v?klR+Bi#4DnL-b^?-5Kd!hO$Y$O!lS8S5%K ztSs9LpL>WQcvyKkEq&v@d!Pg#F5LGYP-4M-XY--NxbHgFgdw}hGEr)6Y^v-~J_%!s z%Z_E>825dNl@TlAZe!pW_q~jPW89bXT?hBQiGgF>H^aa&?u&h{F78Vd)xmw|@Cn7Z z@9!Bn#(m|;XpH-Qk%42}mv5$v``$-y2lu^}!W`W95muLj`;MXi*0?V<(7Cwp_Yh2Q zU+Dm1+;_dr&DjQc*vGR3&> zDPJSE0aU&p4btjWZl-vQMG4k=Bg^xcN?w&UnKcKCx7>$O|?tzccb1!ae z_fPT;IDz4bnEE4phFv1z!$9Q{sM;Ggjzx-4)+1Om-ah=h!%$Wnq3uaKbzAG=$`H#O zJFa3}RZDAwvN`8&cd&lzXhO2>hj=k=M9ahIZIGXxS9xgFp_P@{v4>Sw9yY#m{>6!8 z_d2Gz255$l0Xn9y=JP97N4jy%cr<<@-8&NxjPD| zMsPg6Zm_1NZAs&j#->KV7zSLds&|mMnl@+xjt|Dw06+s@zcd8p^3T4VQzz%?YT5|) zIR_HMla@6rycz=lUGOYEZ6^TAxvh;v1Zx&IuclV!@QB`4fT8F#ZfaQ5j{N1IF%7F4 zT9-96Eylq=)rhQT_qNsw94bSusX6xexyJ|uNl4N#$Q=AePWADOpePNFA;wteF!4zo z7OZ1MLXq&fOX}7}Pep1xhEMezbFPAAv~@X4h&oyoo=Sz3@Q7g?6L`{^#`dh8VhmL} zNAT0qmgY7bRoo7>%h4%EI~U0!v^9hnYR&TI)vYm*8mb`{h01!Ivxcpr-nry52Pe&8 zwi~qMfqPpmV7A)AX3^YK!y2gx4~BOG(i-r*xVd>nBU)8`ov6wdjoD#I^3da4=(&A~ zz;|qC9-b*MKGw|IhB{z+dKUCt%qe+56ds}PDn9x=Kzr*$sf}$li`Ue3r>z_Myc_D8 zR=0Tej-u@oBxfQDd50>WSJkbNEX7>`Qgxd!@O8kHh;D5XU=;NbIHIe@5#wI+6#wH-1=PqUWQ{s~!g=(E6vN?Tw3V*t8{p;|hNa2JFnbD@MOcf&cnrtA(Pqp#9S&xvA9o${3u(su zj)a5b?7f9@OhM*Z4z5XBIr1^g7Jx)Yn-GLE>j`=C#^9UFdZ0W~lUEPB#evnk8z1&5 zw5VY?nzQh8^H&9V8Msj%*1;lqZLquYUWdG~a5H0m`55LxCx0AUSpF`9-QrLNPHITH zw9}a1df46kErL922fbmKABVgrtdJ^?GH-Td!|M5a=hlm*dprCvZf#*0nvWw~e=y8b z5w1TN<_26aoke1OVVHNLy|7K1vHTft)OG)BVVJd+nds?-4m%UWKN1x?_Y3VwjIjBLZ(a(8MK!=o*74VRSt1}WcT z(leDVSGqyz=akA^9pP?M{zpn5R{9I2zgD_M=^vD)&@h;e%$`6;D$hwA-RqUMD7`@G z?(?!?{m4oR>S;GV8P>?Qt2dh zKV0e2>TY_WNAP_@e11=fB(Z-YKSp`>?c}9@!5^dSv*?ZiG?cv5HyKdb7fx9v#r*@kBB;-%pIDIqrsNHwrksVzFftv>2qE6Jy0P;l}uiQi|m2Mf&!&=&+ zE~wJ@^@Ss!=^FU4hr0@q7I4(g;@KB>CXt5#dEhc?n^dzeyXf z+`LsDYmvulx8N^f%vm_@Jh5`d?k`T|V{Y$rps~*G}yf{~d{= zs-bG@q^sJxz{N`oaa@-76XS5}6tvYM_b6@3JxaTWijY&V1A4mdIGualaj~AHRZMly zG!E(#Z^fFm;;HI0kUl5lO(;$KBzwi)Ly@h$Kjs}zgk7{#O4-8=i9C!!!&k_E8Gr1o z?P}Noy?zoTRS^7uT#*s3y85Yrr&!=v4K7g-tOy-tp}Q#HS+7)4z%Ows8BB&>G8xo> zfNO-KUzn7yr+|gYo8gl9#3cMJc!Dv4GQV(T+qqnw`y#zYc!jIT4puhl6}>9q_VH79 zz!H@DP2}nTiRgMcC^+ zddze@uARCU7lQp`#Le3NUL45y$BLW17v6gikdXZBkOkvam5ZBKaXDi9){wAVv2yj2 zhRRiSiv@QL~oDIJ|<~PTWt-L=mto!v|Oq(KeFRBcOR!%D!aBZ>ei- zZ`+R5!ez^AR`;~nr`gS2@Z?%=!;*|Fw?%82ZIsg3(9lp5E_2niEw5|A8W(obM+;vy ztFg8=VLTST!iD9Wr9_dGHAcH7fdtf5s#U;V{i`?<{%tGtGDk>_xcclqjN4S8## z@p^ZAW7FEOhI9!pTxrB=Sy&oKTLp1{hYMJk?JeUb{hfI48T&=|wa^~T%U{b{R1&T^ zaPH5yf-e=rVyq)P)*ZyW;k+_*FgAZJtEX@rTUs2(XShGoqQ?BDgEu?9c*`z(0W;=z zBpkvaW9V}|%25qZmV+N%E604;Ee=DzOAp7vtS7+PmG?3RaI6Q)BQ<&Tuv;8hz4r=o z*F+eO<}CbNd9Onr$I+BG06&wLzm_!yna5Fp$XHskrvjjk3%n%#Z&+FpmmbLt-ijGF^<9`cch9Va7J6QHA&W|dA>%D#yH$0(hvbb-<{l-4O-u9WXI z1#@5hzb89l#BE9v{IIr{Ao(hBt^J(Qmh-SRsLG_ zzh3E=)%^}qgyZH=#(PZZOG^JoX(7^}|Ie5o*irR4>kCrP_+ymMo8j8T^stm><_%-nR-QfzB?b-A)~0y`EUf1BeT3|c5$AC$OJGp-y!dEHa&$UPUsr=yQg>@7(_e=`<5KCf z88_O=J<8!^CwFqpF>p$Z7>w(VHZi;0to#ftQX4PNgtooFZb zzavgE+R04-FFCOsZix}a>2IPsl9NKNG(DGGw3GW8N|_S+4M`tM?&BdhG<_?jPLn^5`*GqmoWK!w&tn zslm3C|VZrk0vfr`j@lNh`){^bjPL?fo z*;LuNtPArkF1vtn za<;1bnpt)!6N)u>J;cEAPVS#DaJ-XSR%YX!+?5O*@8ss2nOvP%2NH~UB2jh(MLRpW zFJU2^o!spAlg>`=PV!stP7$S;$wHQvenGV>bmc8SmsigucD(tY37 z5XLRun~+=oeE%tO)dzBy95_+nb<%;@$4ibm@#lNP?tv8lawOn?ArwOq8$`^3*g^|G z(ZuD?KTtG(gqWSB(P*Qa{HGf?%dbu)4?u(ILu3;a;u+86>Dcl>TK75j&7jnVp->~%l*-h`hD82im^oCOpSTfq7{vf!7*T}y+ zY=?Sdb5D3fV-t2|u3TAJ-`G@HQNdm5mCF_{uB>ZaHMXg_sbMUq@RbuPsw%1~6?^DO zQvbHPf3VoHQQqdAf_PvTCU(sN<-p_Oj?89Q(Uwd*7+PC|iNKB!OB$Ajp|N3e1#ED| z5Err0Rxb+tilDUOvN>~t0Tscy6=SnwgFPys>#Hg_XX&`Zve~L}!QS_sIJ#oun94bo z-S}bx;rp_$$Gxk{UF%_E%`h<-+Rw+&^nP7Z5bFJ!1-)MxxYMxdt-(+9hYh|uxGWil zVwgkVE*bYGqQ&y<3TNaVUy+*7jsY3X&?u;sSKQd5&Ft0;)(}D+JX-7S-NMy5b-1ky zd)Q^8bGUmtJ_ukNv+Y2@RvY|tHG=)kDFKTbQM{d6&Q9d-^AFD+*qI00T)ypl__YD= zDt%tt1FS8slzyf%%xcAA_k>8i7d<>Y5+BNl_P*DI+rF{K-8r8U??TVtP9ANeud#jd z;Rf?)zrLO?snJ6#;of)VL(Xp0=xGW&IDm5iP0d~ivWFY%ZF~C%)eV+& z24N>(Sk=&sGc0vnbY@xPG(fyn|L+i?m>`$0zK{n+dFub!x`^ zh+nw!);Rg&n8oDfW0K8~moZ_WCT~6LuDo@SN34MPLy;nR#6uW|d8fGs^8UjL37&rZ z-K-Qu%t{FA4r{y}zGy2g8qB)|;f5IlN?A0-39U_$e;DEJqeYE{BMrw@p~vf3`CSJ` zLJ~wFm{pdGWp(APM0+_NZe~oL@kU+uU4b=PQ)in?pf&)pImVZAaU2?&(0_xm2+wlA zNqv7(bpc*k@v-Ae1`TKwrrz zCQjp>vs4f*jG1$WfYBfb@35X=v(jgjav_=Witz%Ae5I3=&Qv;IX{}P;Tfy+>E4^Fk z!%AOL%Ig;xPWJqR%AS8v(Mbk+WHR)BR;k>P1@~8!e_N^Ckp*|TBMVgS$O4r+vOuQ; z7h^hdM;55ukp(JuWP!>ZS)g)97O32j1uA!Bfyy0Opg&VNFDQLQ={rhOXuwQoh|)@> zM=719^i-uwm0qg!8l|%5A97^RKd9{a2Nj)UprVruRCJPoicT_6(Mbj>I>|sqCmE>d zBm)(lWT2vx3{-TIfr?HtP|-;SDmuwPMJE}k=p+Lbon)Y0MPWIUsgPDFouTx6rI#z+ zqEvK}A-?D&0~MWQpo8#{V7(ngiZ(Yw>7nX=hSH_#{wbvosQVMjKc(~#bmo+I4&M*R zLsughKl@DbBb6Ve{4C{VPXqk%7`oSChO@b?b}%5*A1r^uBm{%09dO|>bC30XWJryV zUNYv;5Ay?pDq&y79+X&q78tsp?dY63?WQd}3qYJc0bHDW>#?sNX8<+`j_!Lcgf0hg zzkOEs+k4sTw|L$Hgt?^b9o^5~>wa~2lX^1Vyk6k$JTHtksHcIb>IQXeBFC$XvJIkT zk8*4luY~u2XrnurbR3o&-HQi$X}IpmAPQZBEz#ZZ5Xe9S8HgfFHCO=We5E<$n9HW88L9(VSiACe^jxc z>~NTh+-KMp%j13a3Wh%j!FZA|NogF_Vh>NV`9EfN$F&#itsj7{Q`MTu>U%-*w+ITg zX4INuYR%q-3^S=Yxyj{H{=w-&N|_RJL()r_ z_XrFhODVZv_-G^}YRxi?p96*`Vley+3XQ?=I_9kpF#I>jm8dn7nvKBle`1DC4YQX_ ze~W@5Fr0bWHnnE_xKpiJ31dcJI7anbt2HZS5n?d>3y8T5YRzQy6oKJirK}tMlRMvxO_lwCHDhdX8I>kFYR$5&l^6{F3`NCY_!xI%>_H zq;Cv{Uqs&+3~yq(F&NH$K#p3oKQnL)h7TqigW+o!HwME6@QcB4zL~CCvsD!BfZ@e# zH4Yd~aM4w3CNqky!SHn~jsu3@j=B)FX3_z~VE8m9AA{lZ7_ApDoZ})W!0>qtUbOF<7%e^veOYDPC*`O$n@2VV!&fqN42Dl(jl^L1!>o`P3~!-t z42D-QZVZNhgW1jn!yUC|PqOY~Fq{)kSFKqsWjSDY3$y8f;a_G>95DQTigUp5%jxfc z;pfob0mCmL-y0atm7o5C;ZQwR#(i;)TC?9U{cQ!qp@DBdzN@Cz>|j>PP6ES|Z=ry= z!RjsKJGE*8nBdb$E_K;c79bm@p+WM zOYKrWr|X_aVhq&@x2c66Pu~gWq62)+3!sd30ZLdj^iQ0Pj7X$8{4P3htoS9`Uj;Rd9SLpnv7;ldTCPaszw$}CCW!H~m?#ji9ygz1fbO;Q+~9!{Bu zvH*z@L(*q5-eIBN(DbWJeN5<=Nq>bQ$A*3*)7A8=2>nK-SJSUDIEVi<#wXH3Mh20MlDy>6(KWfNn_PJCn7l;bI&}swL^{85@@&z zl~{!;$QV}?tF=gMtf4}YU5+|4D8LJKmZ`F{_~>LiZZfNDZI}Q(a61xXrwY4ejZ1Lc zk}A4-xnc_is^=yUp!?BfTXG%+9+I>10w>1jBV}(us3i`tY_q6fJExs%$6KG`tP#zA zqS_APRyP8g4E6NEimY{W$WyNVs#{qjD#Gg87PmL9Y6uxtE!U(Yjb`0K@YsR?!6CrQ z5;UHNXO)l0OM^#NLXCq0mQppEX-}Tz3LFTYxI8uQ%7XF<2wu==jKKsFzOQTh+eEXS6~UrkR36)`#ekQ!E`~q_(K8 z?gN49AppLO&^l7DV?BAQN3aKQUFw64a#z9Y$QK@>yad4Zx9=+>9W(Yjgx$KMK@;yu z&C=CP(47}4U2KwutLg;hL56xCYjV|VB3pPn`RH4jJ$5O4AJDmkmDnACk4AzpyJ7t8 zgYgAF!>3Z#e)%QiI;{I;!`MhnK8`;XAIwtt491V~84kn0t#JGeaB~gA(j0~#!(#*{ zrl)XO_*xtWWVo+84rZO67w%&io$*U<#=_;}_;XPX;wmf$*S}$veC6Z#zr$eSIun90 zX3fX(s~nu15Nm4%g1d@#i}E zUB|Ltci0)mU2V!DI9rnT1aN5G9hw*&O5W`O*9qo)=*zV1|DD-K^Bym*GK7 zF~;$`9d@*p7W29Z;kqktK92t&!rf<)9C=I|O(Zg}U~tGiB7`H@u30anEUUFqSiK== zFC%DCLpj|U*L_#u`0g`d&GOmKjWXuuhUIZy420PccYR@-hL~#nC9V#AO_5Gui{x=mSeYWJiOz0j6Aa|I60``|pn{Ww3Qi6xI60``} zP{GMTYt>(Ha`1wag9=U#DmXc);N+l!lY{=B#!KO)q|pn{Ww3Qi6xI60``|pn{Ww3Qi6xI60``qFwbtIHCfliHdPo+bY z3Qi993gthpbe7U;rDrK!sq{Rhmn;2}(p!}NK8ncLQCf&@l=&O1bhy&2 z(uGQENb#{*tn^%U-=Op+rFScpeKH8oF$s?ybKQdRIX>z3jxjmg&|C??T}+>6?I(PE zd>FECeEx-Q@cB~U^HX^@*e0Brp9X9mSbYhwc|QaFcBRjY4nB+`q1W#EZ$6NH{w^=O zzQaHKksnrPH-LZiu2RG5Q6B05d|>0VoqqAquG3m>+LEZH{=9jcA+w9JePI7yrlV_W z%S{ES_Wk`QpX&0wf7rrws85f&@E+LssHExY-Lo(}Z~wr?=R4ofy#UPnv_A6AeFez3 zkKbO%cid?FaMt;_(UT_Jb)pUYfy&_DlS+?FW0~OH?Rl@q>2}Az8xdL~9G|cEPovMF3{+Hj0qx1*Mc*Ww;c%vCGOgwG@KRUf&lmCAgO?w4jVBXCZL^}~!8NunF z7r#`Rc?r%CxA0g#<)&`N9}d4%IFIGiX-{s)m^4Xw* z|760=Y@~Y9?K7ID@PDLmLGgOWK!ubGJ)F#!iHJ|o;VJ^6LKXb zHT6rq;Er!2JA4}d#KfYV2rZ0vH|r)bVrY6K^Kj3Jh@BXbNk4!X!H>=pZe;ow^!w+p z2sbLtacOWLv8%+0F*tf(?*$KrDTK%JgCB=^4v*yrKMC_WHGLe@d-xuSH%-(q1dlu} z+>8<|c6-6&p_G|vjwFLdIF>J6Ys#k^dVsP| zF`alP0uk9@fFIU=3c{EUG;-kYK0n2uQOI3MfMfZ^%+?5>ZWKcu2f;{X3(F?b_hA3u z#kbf?(ZZkf8VMfjMzo@R|H7RJ0d^wPG5jeBewrwasBLhn)>yLaS_(fc!O*~h%YH)N z(-Z8<6C*~J71B4}iSPn_Kba_z>a8xTq3@!^m6DUw%6R8qaAx8mNoaXlF)RBlWn0Sj zVO`cJyQb{36jhrjld8qB{0)>@I0e0mgvEVTl}&|D1C|YcYvpV zgYtL#W&FqnOO=1XFT0BTGUXrjy%Y<F`3qVStAt$xtREJ3Tq6EJFU2OwN1*%=zDIAMsqV*j6_4p9RN{Vj zu+V2Gn5LKdKK@2I!LfY4J^moR-e@%R9GK)Av_D&~Z+*55pPMWFgQcH=WLb6rV*We2mb1U}7lxC4f4JQ)M~(d43|`LIVv^YJA0Xg*vcO>r@A&M*VC}^dCj~7_gMIHCf9whCE5{v%f0g4_j9WZzP9lXz#xBa;E5Nz8 z88%m(vA^0!&oopW>DD6dgJb2!3rmJrJT}H^@ekvfJDbuNaZgrL-lbl)(;?n8k-y2I`^^p8vaKQKaC8|eN8z3(pE{k4so4Qw;b_MPK5EfvvSi*i$?-I$qk zRix=5Sd20+Yi`9=fV=WI|L$Y_J5aTFxA?#N?XWLOKK_3L9A@F$!tWYBX%X&wu=4VK zhJ7VH9b;@h{@+tL8Q(ea8ID_#&6ssM9L!EXZtdh3)r|RZErbI$Gn-J3%TY*{gX3wI z7eiJrAOAlE>-;}3A@Dcr39!5JmS@8Flt(!xuO4=b1FM$;V7i7DHCE4Ofp_J#WIS&g z+$fL5F?sp;|2oK<34b$|RzCiJXC}@c$2=x4AOC+8@(?sKCT~4l-2B~}32}1f4=dV{ zynOthI@i0bkO)V4{N1b+M9fbii0cbxEZy7TfJe$9mzehvZkREk7A_zEKd`KS`2Vc^ zFbRVY1Q)&G%xWoqEUUF;SiLD|FSWF&v2=|$>bn26@PEFaq4A|09J3nFXMeA;s6CdG zW7g1w`v+1{AwqwSU0wewhc_S6ow>h&W$12y0k)w;hR==t3&XO(FnkrL8TwN#qZ#tY z;%8%ahUa_D@{R!|e=4XMhL0vNx_`k2?Hs!2>j;QOEHZGR?@A8jX|a)ZG=7s&J$y1q zV(7kMADbBBdDoN28t)yXXoGxqbbnEKJ}dGgL5JeS;IkpmK8;5`ib=7u!@NglgE479 z5FAxh-2$Mam1lj?{V1iZAM*2*p00Gc(l({nDCPUl@ZVJWL!}QZeOl?Sm2Oe`hSGPG z4q`bVr(9{J(gvlglzv(%=ivUcOzWellS7{OIhWtKCiC&OD zUg>O&s55#dxl%9lt)j=6BQzT zS}D)Ll7Cw%^?H&Yq;$B_B~yrRLVBPczY?$D6Ld_q|$jx7b#t#^gN}XReFQc zZz;V;>0?TtSK6iYO{D=Eu;gFq2&IQBovn0%(t4$5EB(CEuPFVt(tDM@p!5}`Z!0b2 zDk#cdrgWsz2}-9cJxOV;(q^R>D7{+g*OY!w>5r8@qx2=E|D|*w1`kaCD5WPTy-ewK zO5ac_dnORR2pu`&?M;d{wvW;S)xAM!i@LWf{j|E@uk>+se^Tjl>i&|_*OZoFP{Q;w zq)2b1(!36`helv38pEl2|w#)Rllev+>HkY0qRX-W#idoqp*>o6)TI_o|=W@_x2pv$t^p!miu=U+=%a zKjV9)U)u71rT}{%p8MVVUMRPq2DX zE{>kEEAsY;F?0+>!~62m3Zw&MS0KHJPdThC*y2mNR=_R}2Bg=+0mu0lg5avWhshNg zxsicW!S6!H*&w(oZ?iZilS@Gh`0)vTL64%~J0Pi|!v6>2zso;azZ`_-a{T-=i`vF{ z{@7aDtBcykoBgEJLvW(mqoxM8(~Xl@segmRsC>uQ;J^kMh7{D<8_<&H;H8r&V&afm zxck2M7)ZkVHvjIZCE(V^re)i?S>N=tV%QSBC$jjum`ADdPEu9+pGy6-a$f2PY&Jqk zdOb(QI-XfldbNLP;*wxPGSpE7xA3NtwV-i|;@sV0C54#bK&2GFk@)dJFnk6-b{y<9 z%rFey7$%Ef20zAO7{)QfCRU5zO8of9*r6=P&rB^Wbfe!I{OHH9X0~^!kq--}Bb?ff zkFw;hF-E7JwtbN`cfBeW8@Y4wDu?N2X-?LVnq~OMRDqhWKzJ0Lr81{*IK&cUir8x{g#_uZ}Unz?NtIUnA zrbUhUO$TpwdJ%_AI}T>eUw4^>a%@C+mV=*HmY4a;Uw7f&WDTTaJgkkU#o}KDdDj{PYVvMTJD)Snfe=t?p5STa z@B2zY#89ux6k|Xw%Du27sTT8UK)C*_&#XtdCoGa9k8<(oBl9u}pM~Q-aN!yc%gg7( zIzhUT8H{$tHg3l9XS`9@eOIi{@EOI%mojXK9NgTnJZy2X*%9Y6Vri5=NIso(yBt5R zZ{)9^IG=5xwBaI0{58w{BCs6&KA(Y#;8_sfDK=Uu+aUSLO4(PCKSt?Xr3;jvp>(BE zzLN}pq0%cz(Ri*_dKW3yZy#6s8>O!+{R=7L-$ROc99Hyi-!;DO?XhCCzUCa`Cw+X9 zOgZ|Y7cGhR0w$VsP%m2Gm3DFevWI=qD9-*~=GrX?^J+44=a%=YF5c`pjdrxKBFib#&U^=AAvS zh^G^*={?d&4|5!EKs=?OyWH~}b^{z-$0M_H*!_p_$0igU_EA3=2qqL9$YDaEgzW|s z3eGFwh`*RnB$G9u!43H1r;3(b>Rf~Q;|HGv@NxVd4xQ0Rc8;!bw|Q|aK$AaK?!e%HV?p-kd=(gu{K3lG zxS<2QYvi?o1IG~z-&Z((zbp={UI%_0*O@WD>2NSR{SE>1q6KpN`2FL!#>{PC?gYbf z^fp!w@TT)yY{t?$0S>Ob)6h#=y^vbH)WdFZD1#U2jG{%2)$>{4U3s;T$F7d~V+U;V z^2f^DQ_XL`8A~gFtlZ+{kNuO$%O5LqM`6ZW>N_qWg>rWoV+-3~jvTFi?F2!$Wmn5@vt~X=bU69 zeykVPJIiWq6jpBo9QZDnp`7lF>%J?-z-$|_aZ_jhbA5Njm=|LtG3+Zi#%237!+MK9 zlo;RY=ZvIfEsjaRdnf|&eIpjacGx3+22UB&f{tc5(5zA!(}L$aP4{Dz&Q&U7TDaFL zj|CP9hb#!aMCrApXbjgY{jt)AmHw0zavGIqS$TePi1Jbn{4vV%Qy$B^+)?h^=NNW! zRlnEGFGsz>L}S=8JcS@ziZgLzx8O`)=ldJ|%?Du|8*GAZU!3>zvkNwtynJOR#-nE> zFcwAjUd4()7v_BXC$UcMm0r80BvZbr+p+G*N4nmxx+rH1?B|bxM?Ea#;0wn*+{JP4 zg_(!De#`N2hU@W(&2&SS2NZh?pljG0;Ah^5+~36*x!9ZEiQavXo$1@fW8}P{@t^qw z!wQyH=_#>$-ZD^eu%R)#YYdI6K`=D_CAlIaUuNJ`@OJ2UEJ!po{vYvjhsOU&@1o#- z5Dbm~K>xe>XIeBgW*&5Ce8f`P=NC14{t@N0%E}aepCe-b ztkkE>x?qoQ(PhCq{9BD7^P8{~Fp7?zFNJldjh^vF0NZba^ z*ge>)or|M4EN6i^hw1;i^s2hH6@P`HbUV~yc@?6xtD?TG4c3PG)yrz?>g!vfs;Hv1 zVWqk>HZ5&-dFZ-oaQUUJbx;W9L}*!E-vlR!jD^(gj5TMD3D^$#4WOyQ&uwBHPteHg z3L1?m7|Ail9s)OxANvY724O6YhyinfSi(Q%$8V(B>BDpId{fMr-{Ek;r!6vE8@vtN zApH2rVL6yDj(aT*tlnqPU0y+p8uObC-j#RdfG|Gg72#*{*pXTsSaArm`|&Z?r(Aj0 zLmt1klt*`y$8oPKFAI6yjgQgXbL8FP9R0(=kvRrvX?P5d zaZ_h__lc3`laEc{*=RR(3#0~If40Eb1XzK#80-T$KDNQ2toMck!w>EF_X*^ghd;rbfO;!*KV_)-x6R< zi8<;1-HvIyS-)jmuJ8>5m3hWcUG~wFrEqHV^<@ovRQ)~dsIZPjGyIl-oYrD~^E{CAu zW|2!ME?U)n44nVJ{4>pGYBWsUhOV`=FOtn7M~MBTR2?>v%$9i7sXA;TnIra-cFAZI z?KF2Z`ZPwPt+RuYieS%*3sQ?NotXMUHfX8{N26Qs*T8EM85T1hhw{zmOp`DBs<2-k z3Ra&AY@x9yLvE*!AV0`1h7UYQma6W4F9hr7xEV7J&y2!8jnNso_c${OL$(I&I~$!D zH3Qlz7c{SKUEI*RHiKIgGvl+_iDyq3J8rahG{5R~ye=8Xb73uj_vD2$=N<1ACa`h0 zqHXP}_PTn|_STRtH`_;G)ETZt;p>t!?sS_}5`HxD;)B|k^96B@|4;@^{+qQISB956($VS6x+e z=8rpvI{E8u-1+|??>!R+N_qU{k2~K*!5evznEH4D~hc zq+YV!eg~n@LB_zB%jccpapC)*|qknAs$98ssm#cg$ZyeP|A%t^pAg)y@XIwtri@`+o#!7~OKWvey;)E4ymmMx z!h11yJmk*DoI6e%>(8m|VGpg_4=~R&OOm2u00VO!e%%I}jm;n$T=$EFR@e0ZP!GFWabrQ z;s!7*2kXGfF&}n|1FJV5%o<^}U3u%kuw0Z!YVzt~w>YqRPk|XliyEtEmd%w% zeWe`7Q67d&kvx8@TzPMS>26%Z@t!O11}A^`#6|Kh%ae!0CSC?^W-P7saB$^yIQhd` zMkMb`urm&yAI*5k`%7q*zx3npW~JhYKU@c$VvOU*=LxSC^I8t3yYl9*A6|-Z_gN%I z9@9paB6BAO-TlGWH{zjoH(rhesfF^*^u2zFOgg6T^+J1F!$<*ITJDOVn>y=_EhjcR z0$=B|reR;faoj$_g@4#yWmzHYqIF=$UOHZD;JdLaG4`IudY?uij(`lrZ~3%nZT=6C1e!#fb5MI<)Uy+G1JkBaRb&AKi}_tpjr$qvAD{w`VjD4=08lzRlqn z^jfJY^g5t^BRzQ-2J48UVFw`^20Z+Eaz#csRs|kjfR_i3>>MLJ{4Mcv@$gQ17X=T3 z01y8?{r5#gEGnK3I}P)I(dZJABj||nDBlu4M8{7`Eop1_{Nu%EehTV~u*kTS{wJYJ z1(_p47wz#bnYn)yA1pcYqmfK<0kc599~Z;6)39)~=-g55Ggy5*Z+2C%M@6tUb=t(# z_s3s~x5nQZ2WRz}VNL9ZU-*Rn3>#-y>nD9~@|x3X8z)_;l6Qx?BD@PPIJ1>b(qk-Ho$Wz|WP({hgM--qt03JirVEtFg55*Cp3E z`Ri?6vK;b;nGndNl|QavIL;m~>FjPidn>|0L<@+Ra}lmT*j>%xhaxoZesIc!o%4IMC+2$ zo9plupDOkhyJB6kmg(RF+NzWvN%EH{Wgay3#+7 z8*<5wAwr^2W4#fmppaZ33bj|yc8{x_e=6>9)b=k0xDXxsMM;15-n}1+9{*7 zRcog~n3h^vbvyTbqCrB8s-!x%<4fA`v4;FS%@xprR@rC18!h7-3G1Wr+q?M>fjYe3`Kl!t4nn7uD%`#)eYJS$%< z;91E*foHvqwqyh4uz_cN!gItwI;i^XI~aW78Dhjt$uAJnv7X}pM>@57m^s3u zJ9Dhj#{tBdg9fxSKAI!{)bIU^Pw zP+RqfFs+0pmxh#K>YLFu?N0vybBk*_<^Dr2vM_%>ck3HIIGgLUogbPvJ(M3kOmp>n zGOK@Fg4aO*l(e_CwbYfjw_#c6fEB-JIgAAP*RWXWq!0dZj=bR2+23Li87I z48oW=Fv?z~>uFGB{Ce}VXTpO|h*2ioxo`;bcbj18fE)S6Bxsg{`RdKjz6S!k;BJ(m zR|tn7z2X4Bm<&HdZz0Sk4r%bn@KH3VGWB;A>_K|mPn8b0{`gr|WMBMUZ+@2iG0R7K zN8)Ec26|D1G0NnMI&`@es&rvVj%o1*Mas`-rn=G4*_H_vOr6hIut(9}GxX7{?=p5VQr1!_WVq5-z zdDj@GU7`GWSnkUY-T`K%d-s?9^A0e*VrvoxpA&}J_Aoas5&U9$mGUAufgPT8zW{&EYQ2`2VpV%^*DHkq!W-q4`2}eoz z3NJ&U$mf{omA8stSgs{=u3`nb-fdo_8@>;J_d`nso@VhjFFGCfef-U14LenlcVTv_ z>B2NV$20=Bd9g#e>GW)x0ON5dz@O7V3mm!4%lV2%#C)VjRlJ()EB7bDOXTK!rdAE5v-|P7>)N7F#mRGWz2NdgKp-b}(=(OFi7Ikn^L=+gZHCZC>->?o4+6MT#5B zoI_t@4QHOtb}`n}%u^URahq2b>*joilZCv^OAeY%+~&1_fu}kwE^qTCyd2t%lxkPqzD;U3Iy^MiBl%xMglxXtTYT2pMV_IaBZ z(-{)V#s3M<;SUp5R+@v`y#9;X9&Xc(WL{;w5o#Tjc^5M^(%vDyxXp_OY8f?fo7YP$ zMDmHh$0y592e)};lG^1C-yNjJQ`ej;w8lc2owUw(7@D_v@!QL}(&11pw|Oyc;x;dK zTj$e`E7e<^d60n@I5$g9=4TG0^=gN&E@yac=5vg@P_50GBN?|utxGaTGE}LPB2|mq zyq3^j=A0~c+~!rv90YFjk^_z_B)Z(@HH-e0YR7F}Z!>#X|65lxo9mUSB}9;WjU3z<0)NUS|gFxXo)e?E4+u=Ea#fr^%t6)w-0{W~W@N zxXp_O4qQSWw|TKkvI*3l#ph^0y6Q=MR?$o^qY_U#pPBrO!!)h%2K@Uu!EIiAdhB6* zywPdsIV8j<=w$X@|2D4|#r-+_hxg!*eM|erDZC!pUXCo;gKa)9RAZoVTNUP=FoMa@ zRcxKQnMFCxWlEUN8bh0v$^T<9Vb{+LkRDK)*Lq#+;Uus}IpxPs0h#eLmrJ3h{L7_a z)(fRz@}~sRS{M!Bf>y|@%JXiBI`aGPsgaB|l()9DH7%}aHN|VmpM*<^u-O`AL$$RvEh?!mgUmm08C6Ne zwQZ;n1d|##^e&&qx~6hmgXG`NBPo~!dR%40Tb~;0K+22ZUxt*pF-R^{Vz9-${-@_0 zEN)vWYpFp>bq&q!Emd+pYDrmZMWX&2s}@U}pq?Ar>)E8BRM*Ny4E_Xe39ivuQiqiX zCDnEHXe-%c4$?fC7qqLJn_4jo{K2PrORCwmdH)eQc2m11jx(~oT-0K(QsQh_nF>a) zD62G`wZyweNIF<~py2-OF&8cLu%MTDjoQ>$!V;B8<@B+xfek24XRa(N#}+E9#JkbV z%SlaHLqnPLNtk+FFofbDZ(c94&@OCh-p^E321oBIBFn!!3Ab7WZVYNDZwWj~hhAh< z*IH7sq|7UAud-EOqgNvyVqERy-}W@Mu7=A!nH3uz8ug<=hwJGaOzhhpe$KgU z8Z-S%p&J7Z#yA!3#tftNA}oB%8D;#)Zw8#Lbd=+LWP;@&r(?_a}i6{vz#X@@@{+DFH+>W z=CoHR%2nO4e^KqczMTHgD88u3y|r{tMOP-Ctay&%bj8_|PQ`VKk1IZ_ z_yfgP72i~RUonhFnEA+3%u_sHafad*iq(p8H9O*cPVEmXeogU5ioa0Yr}!tu6wbRK z{z;0X6elWPs5notQn6KWh2p)6UsQZbajW9Xid~AKu$OMW;xxsjimMdgQhbkyKJ^F1 zGz`Sd_v!4%aOeAQfM;Rj`>w=4Nw7be+r)7L-}|tx51CuX6r6+IjC}|e^sXW*qjG!rRuk1i&glaMBw&b#U-Ga*y^Xk>AHt#ix9I=8@9jZ=Frd;3_= zog1h1+PN`_eGdH$Z*cTF%%kuR3XN;o+~j4XbO+$5QaX;ykkav5DM;xCBao5OHG@>& zQo7mjaStP<^J0Z2B1k|=_c#(ftdwpO5>+W3Pk87{N=NCTO6k50Bc*gAPU%}pw-rH* zBo6X2j-)EHOO3pP6pU@)Qo3|dq?B$1WBXD%-tX#6$U)gC8yrhNUrHyF%D$8?i+S*+ zbpOYY6TNsDkrGnzrF7)&9bZcK5dC^e>25(}kKKrej#B1?lHe1~97al)!;nAl z8lot2Gei1Px=xnQm(tx$+Xt4?QT+S2C8guKCy~-!!pcoZ=|qTTaF^i4?VirFR! zDV<1T5>mP^vj_<(oiNV{DIK58pp@?4Nh=_w`#e()Na=1Mwf?1aBC-fb>7D`^kzKuY%v;|HX4%b8q2O7|M$A4y7gC5rk%q;&HAmypt3#QYqMl#b7;kj<{oD@bKb)!wujtlxXQWB zy(JV7*-Zwg`dc&uX38nh!}JMNHZ-~~6dFYpX&7~EM0%X4r?4j>+4D0wa2ek}q;&2t zB#{&0iOBqpoow!(Lb*oDwyG+lB6owx&=bm4G+}SvMeqE^iRLU=ES*ErDB5F)Y~<9Q2+rh19f||3}me5Vd-M^1W>Xl<(~xpnNYCayuj68-koL zt9{A$CZoTM^Rg=c!8Qmu_h_4AA>U(LCREQ{{UzjitRt%F(3IzK?U+%lx2UlIX+N`< zUFCc7-hzZL!I*;ilQmKeW%XF?0Zz4{szJ|MAswE(6eXfvDXTjbbL@O>k7jQ><8%Mc zClxt{&@Qq*pvd}wwd%f7@w18|>x2JBwSP zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{K#}zUMb-xtSszejeL#`*0Y%ma6j>ioWPL!9^#MiJ2NYQ!P-J~Tk@W#Z)&~?> zA5dg{z;3)a*{^@6cz}raI$uDy%KsUbQ^R%bS3lx=(*5E(9@wOL+3+}fgS;!4V?j<3LSzz0OspmXg*JS zpx=Og1-bxrcqF|8VYUq*TwgSv(|CNgpMc%~y$*UU^lIpp&`Y7)pzEOxjmm&|LBMSM ziF4sz1U(&kGIT!l80Znu+0Yr#sn8})C}1{jec=Jn<~AqJ({i@^4d_>(cR+80rYvGJ z^b^nTrK|klL$C=1??l$+fQ8xU<`1N>= z6nD8+hs`rUIuM3uf~*7Lj^wxQ2mTJY7WiA>8sKk$tAYOmTm{5AB5$n(?gcIbb_16J zyMRl8yMS%LUjdtezW~+){|i_Pd<|F${5h}`_*38l;LE`Iz?Xn?fjY8n_6!3RnkR39JDw z16Bc-0xN(^fMvioU0169%#yZ$un>~jG z0#FJlz!9Ya8z1s6I8TBbyxyT(vbzirPEPVp{F!X~=TtIP`)WzrKG)7cC>&I|f$%8_ zgCl@!ey!Q(d2#H)3j za4)Opj*IIK_2b?TiPhTP+}zZ{Sl}NU7d195Zj3g!G_^J1#B-)`X;o`$Sxr^6t*I%> z9VK*|Ue(%G*I3q8*VGv0J&XtvZK?W9dtHl=G3g(gIO0z8aw$uRurrZN$x zH5yC;d9H3-T-H(*tte}61xc+<$!9q#i1epuSxv!^afI!WpO&g>Mu%Hj;~8zy)~d$J zXjwI~6K(YxtF^Wrn`OLm6l*<8LzG=xR#z{@HTBojT-D-LGU~apsd21F0^_ZTx3~o@ z5|y^BZZb(E@>yNhP*;Cl&t{I6U+1@P)C*wEY>HwlMm00p%vuX%#k$PT1pAoZTcXI4 z$&NJ_eWRhQ@wzDP87gn8ud9gSPRd2C)`i$^6h*$8%i3zAyn-^&i>9NqV2=r|G9ulo zhUT{Gn2&}gGy%6Mao+4g>=2Vq#voWmTwm2tg{Ea!W&KLTB~=yCvS@29nywPbmba9F zWTg+UhI1f4egC}6<$a29Otw&a0^^igP`ADTh-E5*IK16m)EtlwU^aL zE9zP*+Uw+Tt7&SivSwGc%KcJ&Y+9-sn%dB}(I)5+s?%ks02c-0eas*YAxwQ|srM*#B_va!kQWY`*p95c`8d$6>#>%cy>rD;(W2AsO) zszkr_x+~(BRgP_H#3REVs6B!CsIRZ8L4C>gDcD&~c~I)Bs@wP^R90PES5f8lcNr+X z)aPQCp+~B`y%mi+Q_9T!XJr_tTTHY>KJdghYVY@-se~IluGc%6r$+3&ti#}vD4KO~ zE2`A%`*^TSdI<;SyS<{;Pj^~NQ)}y3FZ1$ffUZ2V*j(4z){3@ZvG@d=_QW`Wf=p!t zDHVA613&w&z7MS8s+NX2JQz|tjdk+SviwzzP3<+cUi~3CX-_`$c<1mzlII@zL`SQ~ zi=|PX2WgGQ_J;DR7V82&@EGhG$^!knr3Tr@dn53Y>RDEn!Q>2emv@=}Y+THDh4(C* zN529yn$pIsE6ta0m%M;lqE@|n#*mKOd1ilGq=oOYF+Hz* z+IL}MPcdodVPz7B8g_D?}A4Sg6s*w$DUPdZhdJxv8*;+jMzj-?-fT;LXi{z3ZL(++!H* zgdz4sDE4Jc_4#U2miMj1Jy~R284go~hsYq8>9+~IL@p*Q8ez$AafaIEI@{%;tI@Qd%TKt5meHJxYSQ~Im{-n zFj^ZC2tj>i{BDFxklvetavTRgQ;z#!HgQPg`=A%4L6z~l2lgPn{ek=$86WHCOVErH zgQnUGdR!=I6phrI6pgz()SJ&Rb`#|pIJkkmh@V8bkC{L~6AmIQhV#{x2=@cumI%l2 z-XQIHJ_lJP=SBc>)*|Z#S(dT}>80ZN2+G||x*nCpf6)C{$SM;lv-~OYE=#!Caz-Mu zuFAWgf%BoB^8DX5ozSmM&-a6$Kgd={C>PSqpu*k9z6-}B$~rj?8O3_{U#Y>}ir!nP zr046mXFi%0$vM!zL~*I&GR2jOs}xr&u2Ed8c)#L0#SX;{iW?Q5P~4=rS@CJb=M*~? zw<&H{+@ZKr@fF2a72i;dEACNzQ*oc-e#Lhc-&Z`K$nM8_;Vc+YWV*mqwWlj`PLuvw ziX2~Pk1873)hM-(QH&|(D{@|x@g^%4C{9*C?)4ykBvhVu#`e#f^$jC~i{RtoXFzbBdjc+Z4Ae?oiyR z_=@7Iif<^!756BJ*qfDk#pM&H%2k0 zn6JorZ~9MGEKr=TI76{Wu~>1g;(WygilvH`inWUMip`2`ic1tZ-_Cq2Q(URIN^!N~ z8pXAW_baYb>`>gGxKZ&5#Z8Kv6`xjoPO(#Qo8orG9f~^@Ur~Hj@eRee;vU5}756Fb zSA19TeZ>QcWEfcwAw^kB0DG$1(-kR$U^rQ00L)f;8M-Xod&I6elW@SEl;~ibaZZ6iXF3fy{86$R%E@xSWV*=vKu$ z)%{-;MO1Mbo8 z`)jTnu!}o(Iiq?h(e`;6X4`K1_LWX~6*Y2`9D&hs*|DvqxBV0uSKMdv%DuFu$|WW9&5`h48}`Hz{Kp`4{PVaVx&YLhZ#~L);gp_J84Tc@HlJ` z1sw+py`@BySHSCSgn*QYe0MlGi42{0MZ_Sxi*aMVyol-JUNih#_Lv<07bg!QjWJi! zGB1q%Dr4GcynOI42;;K?&x{kM)9g3#kA;&K@Q#IZ^X$T+ zq7~ldzNskTK`6+5mZqg1*W>O~*3|U0RM}2)t8|tk-Jb6eT=XCvj zPA1_R;mWG=b{x9b0x?W*I;pw6oRXQ=pd03=8*Av~#X)shxfPHza0Weh%Ir&XA&>Z= zmq&0O0S~Z@&+KOGa=~tAI%Ds-qdk}y;8>mk&G-z5*ViF)N40PZDu$)%J$H1ta5026 zaTt%`meZih_)Ue~nCbNvG@mr1j33{J!8wh0VR;&EEC=6FrW_cude9^P{4(5(GW2@S z9X*la)dT6VJ`BBbm`xngScSo03=OJGJuiekn7_@SXX+V7pC09KL3*1(FGpz+j9(MX zL3-N)`Q!Ly@^@Px4vf}MK`%;!DnoAt>_K`v0{KgWpP^^wnAm<)?}6TFh6wC5Qa_>y z5S0M}lZ*vuqC5sO=4P6W7uVKap73OIi2i8 zaEU##mp$;h^JaO1gT0Y@TJ;g2!?o(dKZ37g&K)oyY#Z{We9nl%Z^BdFv%t}G z2eSVWg&zhAKMWLp7+9qK!VkkyW-7?tBFW=jUpH3Fr5vGGQR@*R`r*B zqVGsP5l_m4KZB+`sDf&WW6cF@{qe=)#+@_XyTqdZHMN_)2gc`%PsS-?ynijDb4|Bv zWp`Qej$KwkM;GRvvD|1u*R>y8{p9?d_1&rIH$PdKUi9ca<2vJ8&hOaOIldz9j$7er zd)6-0xgUO1U*RVN!aK8ep*d5sVh?v`M<4E95d#lCenmWFt%&DfT_$D^Ux)(F!8Pv3 z0r9N8kOCYJyA4EpK4@7TUFM#*ARQZd57OFq@Zjs8{!~ZTpxBDtu2sAXz5Iniv1z*< zwd0Y0Y%1)hs~tl{%HD$qpS|i+1-r*H9M%|j9Xxo4jr4Y7EgorQYS=&EKIF{Uw4cCj z>#*30pPU$5fo1l^yME65<`Dj82%nDpz6^TD1JUHKXJhTM4Vo{4CRVp~ab4$WxBSnn z{g>fPqmGVUBk+CVSW|XqS$B5Zn@}&yaOd3`v`KSEm(}@dH``>SwO|-_r-Sc$)H2U*f8_+Mg zrukUuVx-5l=+B{}@%_tmpMbdy?~=)IkK_3c=4C$IgLax%BYZRCAndXvZ(fG77}|G2 z@56)DJC3n$g5MgXvkF+Mntq&{pgYZ#K$Dj^%sbSbYZ&RjK#^-7v(;RHdp3liN-o! zo*e7g>K5F$chxoPyRCxC-3)hMMrV9m3GSS+mc=`rsyN;1&t2brUvx|S-fhpvt@NAY zop@Ha;92E!9f~fGyD%4>yuLfxx+DJl$SOR)GbJATRucLQ`xn-Prr66??Jh^(G=1z+ z$9gbJ^E@%O!imLJ3`OyL|1;q~IM&gX68rvM(qjMnAH$@GehoTR3vy4GM3vSEcPj`kvT}d9oaF1BC zyGSnw&t@^|&(OaVVO~MrvN|6^JuKLTQ$8LHqihkh#}w2x!~UNBar!UZ^?UdawmNog z{`0~A$%@^+J6v$@n`}F~bK1~BogHpy^!o0!SbaPNWw>YBmbisy-9_DRMcq%ysfgRr zj;)xBeVt`_CCkez>xlV%lLx#)SUV_#8M85)-Q^x%5Pt!FG^6{zo`E{M_Plsk1mm7-&Bl8m{?wQ-*3Y9XYsNh<n6`TQjMKz!o*?wu#D@2*5YNJV-K z=cacY{|v%yKf~*nk@T84`jUT++t+OfxH$GT-xj|d;Wi}m8R+OTeG_#nebdcZvp1Cf zlf8DqN{pd%yHe7u!78QSL~UAAWk?QWs{UF)8`^kW*Rn_X6M z2k#tx%%qpnd0U+Q>Ncdy_*uQ1<(2Ga5MGxugGJ1Anj9-uQsGbH)q_My{u2R z`FH+&@b&q~m!b80ROiV7YXBssSzY%r70`J@d)tnddV(4&y#~wVVPO zKYoF@rvJ?k#4XUcZ{eLJw{3}UW1rn!9#0#0msj3h(=is$>$)9b zh;mmsWN_-gRwBB63<9R0o)5#IvE9YpXtmU zj`vV!HPXHuyx5)H8J!PxPe<5k4EV%M+!eDGYnf4j2d zcXl86f2fxjpT{}7{sr~2Z79NPy(~e!7+zp8{Px4oj15d91OD;T6VLWHSdUhs9?5&6 z|9%lX@KYTBZ$!E4ccbpopBFwr>Mp)w_iE5!{jI`2D5tX$yl{t2`po}I`W4^#Ci4#- z^{rWdKKK~his7$4Z{?Gb*or5sV$&amOusADYK&W*=3Bw@Kd@_kw+Zv`@GbGJY?Huy z!LjG;!a5XuD|q2|3OV%<3OgVZhY9XfV#+>*A*DT*&RKEWgWH`;*t&PE&FPcxpUZ1kni z!`%BFi2nHUpyG~~urKI!?g2W)zo6sgv+-?ZFkhRt4DbDA!q+|BxxRZ{d`tXz(7ZNnrSIO6j(t(l zt#P(T=McPa$Ct(@BTvD&OAFR_ukG9tPwVAg8C~CfPv_RS#M@mO=YFVQyy+V6cfHaa z6G#66KPlruk7uhOeg_b`#q{^_dJ_Gvk z#~wR#3=rRNqDt|f4?7i}JDD%zcXAYZ4D=EA=p62xRwIlV!bE`)m? zz&g0+$*Ub3!%tACla}O(ZFCpVke<7M(iuq)PwlaJ=y*iq zL9ZHZD53?d!;t*26Zm<*p*@SExS+h>Da7U8gfTBjI9K z!^sGWIM3kzbOHn7od zHqx_&!?FPEBdGoMO+M9=?L{X0fQa zI}OL10%xb)K zCAS?iucP z))X(TD~DW3Vi$QKuNYcq*)ufVVOx;uYS;T_9>RLE|KPBq?72hRuD9$zI+B^otqe{O zQw!dZ;-1mo=T-ABT`fbJ6e72o|5 zxP$4@G+x&{da?h@v(2)G)gTn72G&A*xw+jdS+SLI6a1F=6!tk%H*n{Lcx&kX5dQ2` zS8_kk%6J{_8{ssxv8u+hf8(WbxkvSF&o*z!zccs06`ON+hL@dpJkN#Ju>S=K&W>=l z#EbYNlby_+KIyM`;pck9Hk#OI_g{i~gs0-NRkcH%0QSRjmYl|pU`L7o)Z#hF8pO;G zd4`#C>WPeV=a?~Hht)P(&`19#TtRWLgadY1X4WJ4|maJ6!Ce zkUk%syf%1y8fVzOsbkqXDU-+;;wWgYtgNcet+h~ra<>$WtWdq_%)JjEfHPuFewKS` zczet(E5zQ)eD@UROxvw0bWaISCZzq$J*oQP8aE~PUoVMO#>UOO#r;p$*=*ZxYN31a z^4hvf#&?X#%X3C$xY688+&|q|SbK?kR<1jxu*UtIo$Ee#T%-`3y5W*L~iCVf~7+wPW4UxhvcY3v1oc)$YwVzH?1Y`1CQc*thfC*|Kb$(xaBN&QMHHPaN+aan+mT9 zpXa`D144Y(3Gbz`IqrMdfcn;r)$XR7s#jd>))lT8>;BLw z%qw!QDa>=sY)(>*c9jN|rvzq9 zKUyXX!{tLyhMoak49y{M0dysFJ#-uNGU!#%YoRgji6#xwA`Q9`JD@j0Z-RardK)xH zp;w`)C>tBuIx_dn%8|L_kpTbC^^DfYX(O*2JF;QyNV-cH%PPd(TG$$BU07A#A_m02 zw5+9~7C5E3rH=W#6c?}+wqqk4RAIYpAGD^l*Rw`_J918xrG=jp zx5pR&#c4jSiZsK7IA-JD^MRwDj7=rPbUppD|YcQZ8O;5*J&+}6)2{sjnI9|%v; zK_y<0%nS2Rnw`*Cl_Rtb0lq|8mRI4mUc1mZ#-J?&DqPDzfuy>*ENK9v#)m&th< zM60$}w1LeoX~5NSb+{Og*W%$C$7}eQm|OCCB!6gzG;|+ZxR_2|V|9~P5g_N?W!S(> zl*oBw2p|{nF$cJEjw$ky@h*Mj%Lr%G9^Ffhk+~$vyxb(23M_hQ$G9QYAUW{M=G9p( zHiye$p(Z9T-n+gLuC-QuRavto!A$*GdK)Hl&uq)3k5W38L~KCbp}1J?>NC}$)&6(8 z`lv;=vtg0ZN4c%-FR)Ch&pmFzfyxYDz1J!27>V~5JhSkmW2lt(B;R9j7CYaaR68+f z1Qo+lodwPC{e>f+YU02s^mv%MQN}nG4#rHsKS8*_k=ZEYcOD!9@KX7W?%0flYdl--}v?0Mi2*COukjP?SLU+jf%2s~EdU7q8SF>E@5MtSH== zF2{3Ij=NwEBx7Y@){$%7j52H_|&E+T`!wVK#AKwB~@`n>460e&2rV9JsEEKf`*;^AGtA;Q8}>x1j%Ie{Az3inl9Ij0wx~vd1Ts zD?K)7AC|DdPQ`7C+ZA^x?o@n5@m0k)6yu6}6yH?br?_A7UB&km z4=CcHlKSD_H-t0x>S&YOiMV`+{dsLBUHqy>B+=x8Kkr-3VSDd6cS+PKI zy5bDQBE@3Gxr*}@Imerg7s*;82EEP1C_V~_IqcBD(=n5&xJ|J+$#cISOiec4G5o+| z{0w~LWe>;4ClPdb4j$t#!^@aBmmhK?5Dp)6#5aj>=Z7J~uO{Lhvq5pYVqB3+Ug@8u zI34JK<_yKTit`m05RrB%@#FZ|RQFoNX2mul!Yxr;s<=%3S1PVjT&=i9ajoM0isbbi z)Denz_;s(~9Z+?a1LXUKn6LOr#i@!HD;6t?AOqpa&oSO2#Tyh?Dt=b+9>tA{-&EYD z_!Gsr;@gS`6jSgDVmeuha^Vo{&1#o3nqj|F?Xu4m_6=%(Qn6F zF$re6^QJ!Hctts@8Rn_9L(fz93Pm}q8R2hI`$I(NM-=4-EckzqcD(d|NQC`0#l4Du zRR4o&=fXLLKaq&=XR19{}GWdxg`$i#6n*FW-FEu(XVe*`)AeuCAB}P_8n@MGoulHuC_yr?T_b6`X!LO zDeaRL3lyg-&QL5;l>UnFbJadyae*RvD$-k~xKeSI;%db;ifa`!h+yi-pR-=Zs9o|4 zyF7pRGnlU7`1Yr!qQS4vjXIJc{Zmw@ZQ)10;5!Mr3kyFJ7g`7o3++l&%pJje}wG6PuYB0Byt`I zyCWrc>VC#Vp*nkR)LvH@*=6*>27F!T4BOlA6h$%*!joUMwzgIGrNws)AGaaj||TACN^{WvzhFnJuH39 z&dOBgq0_lXH{I%W08Y5d09RpE@-yY{c82qBaDQd6w_z+6)qCEu5@Q>OD!!jMpfDWm zBh`*EUeq>412Xu)auzhh_ZKdPFeVO+BG0>>235vyD(uEguS;R!(`A(LI}Z+njU%ug z<=6ydIrt7W<(Lh#i36jYMg40UR2jdSum|a75Txrbht5pe4QA2tuP1am4Y5VZ+vC^n&1+ommkPqD*Oz++h8_vNaRM)i_)OV_^p6F zNN;i=fA}Qu>2dCiaoB!T4}%`>K{1L(>PHlf`(|W_yq8fX%42WT1S`I{*@SerQ|c&~)!T|a#0`u%=HN0skK;OPtx9IY5roTzw# zVv*t;#j6y{6l)dv9Fg8K#d{SWCZdBps`xu~?^HXV#{u3!Qjaa81Lwmhj?a&Z>VLmv zJ^D73&xdECC2*Q;7w;j#nr`92(emK6XYWSP6g#$a4|!-i_TnD&7q~CjE&Md%aBZ*^ zdok05IUxJ<%Hp<#`%K~w?fKZjyj_^R5dQm}zb1cey^S(@Pp-#Mvn8n(LrcGRzD3g@ z=c|OUa}fXx^BUg z8EWRpFw|!WuM8fV4E0&UEzj~8>a&DJo=t{2CV>``p&lneipfxqmjL*H{4db^d*Gqb z%@5B*=iBb}xp{fxYgeA>4$jTHhRAc#;U0as=%-XuHShBY(ByNaSot_C>z8sPdtMy$o!Ls2uz=9C#K{>l6*p zkLnEQFm!+6#vqJ|1Ebulay<>IjNf^%8#BGgbMa|0%J`iNhaexf1D1_&8wSnS3d_NK z&4e~_U=(@JuV_$Z{0dUzRKI15B>(iaW32_lLeypHuZcq zv(ey4{)W<4)(h*MWktHa+6;#v&u8fQ0n`w5@1GA2=7i}5Q>2}_Nhnu(YS5l+9hnaf zwixO44BfxHnKhA6ls_9jxE9XjgFU`i-V~nfmE0fvI9~ic@VTALCz|1d@V8ZsK6~5$ zJo7md#}Aah&F_Q1b*=O+oLk`S(~GX@Mt0u{MK^UH%wN9So-}Q@JsDcg(Ac``ru?m4 zH%)?`3?19rMcx;C=P?hxQ{vgN{lGb0hSw#J+vmKoP8M_CSf`K^XX<&|xCRC1k&^=( zs(Ku**T>9p@HD*_SohF)>g2I06|;B7i(HLlHc3@6NRmt07*5ms32n&+I$*<*d87bG zSkUd<_r)<3ss<)qi9eSW0CxD>fN=643|@MqUh?;0wErLfK52W5m1ULEdUY6_j%9yB zEX9NQ60oO>W!B(-qvZmLT5N?vm%yqg>OBe5-}KBcI`83`*6)F5AD18Aygr6!!e*oN z=2dAosuYHkpuIMH5vG;U=BlTQtHHS;FTLC<5qHgDRsUQhJ@=|^=2 zG+BfG!u94;NpC5%QHEY`K9zHCd}53;;d=9_Yw!x+gh2iAsoYievLOaE^a^1PasdSx z`dF_1_*AaPi6Wd)rv7^KsWU*2Tta_*D%a=qHIKLmafA7rAIKlM6{gGlu7EaWg3Fh85#(mN`7`AeCiDdHxNE`DJubm?2k`fippdd zqfEURyTPFQvEWnD3;`8PozGaX2hjfE`Bb(wRVlR3r^?$w*;KruJ}8em8niff8dEfL zuza@Zet}|<;v7XjGxV=lY*oBo@eak$6Vb09Q2d&@Kdbm%b!YnybS`$We;ahPe_nOs zIg^f-SLK=rE1i7iEN?!xvtt)pHDwgeEU<94+`)8vw;O$FZxrhvY-=cZ+8vp8`T+_5 zznFVX%(?g9rlM|#*T9K z(d^?DCK6p&IMoVn_QD=nseE0bFxGajE6mHEfjy;p*9>Gm0&A*B=T%RVGULzs88qJ* zCHiuPn1z69N&+@H~fBBHo=YMU>%rp%!b*- zfzjgKB|o4+mGPSidyrlS2X(lS9?{S%huOq|QSP)KMT06+&m6FW^frQCI^0N)1G}Ne z_jZsT*O>Rk)AnA2@N^)5U;})5xAmg;2hfYc%_x)B3OEGw*BQtk-i|&!wh`m7{iset zLx&9!*lDDGL=hk=2LznmVWLZrgg;6#!52rzup=ay{gYNzFw80dZ9#c=KL+4t})9QtB(bL#B63Uj{ zJJWrKU~WjiXXuY&O#U<-}DA$pAB%&&t6Wa;{yuLV=h z{_X%rE5;Os7lu2Zb^2eRSfnVtFx*Sk&X+C2U8i`9;u<14$y&vy6u+hT10v!*sdhf2 zWa_fiF6F?VLDmE5q3n$nW!Ny>&@7X;{jCq4Fd=`yyl{3h5<&OP7Z`^3# zI8*MWy$3BX4XjDtOPZt~wU&W)X?*__?;Koa|7dydqnZyk$7lsOZ?+|QZ=VrQ^^Vv& z?ht-F2M?Gxnt1&9(^C6rX_kU!!;fDBGx+fZ0Py24(3WiAOR#|-|E=eE2>`RlZ;4}Y z_W0-YNOsv7!HLhK?WgD9KlwzYBpkCn!OnpL6*I!w<8ff2OKHYxi1Efei&wJbvDe#k z2fqfO^7!+KBw$}*4c-srdn)KW#~Pf3w2I*z5pMo`NiMaymxFa2PyceyD1|#Xd95Tk z9TWEcD3&819Ty$^Q1GAYL*}*3mxB!T`sFa*&dVFy7=AqOqVL=i{*-(=@SPky<0Bh( zqu7|!)3WZeS2?%2w}x&>S~*DGfnLuWiHco`5P{J}B9emkO|kRO^iP_zphrMYf-ZoL zL38wAtB-*;ieczh0L_t+LjwCP;iA>6slL*$0mQ(efAgvI#a|kN4w6)25vp9fd_LqoWgY^KIs;nGmuMLAS3EmUuPQUbGYmft$ zEVuQ#hPJYDU|WlawZ`;U|H}DGoEFjdH_y|i`vF60?)}Zf{e)rokw?VCCgErwsdmG~ z?Snn27?!H{H_ze1@o6$~7?0r|qd}GNn+m%z)9XH1I0_hLjJ>~kc*xCm1Y$YJo3Okr zNAGW*Z)4c#i(c<V;_NmBVcE0HYO01qDY6Q_sD>dG>)`I>Ix5DbR+V|2Bmi zRXRLQ_l)vy((3)qb0CmE7=3!T!EEA?z~10Y-(u^O^kh_040#aev$z zV*wg^8`Ny-0*u)qcTY3y*8_22wEmRT_qP%IE~>VfVW=jcQ3vvEGz;O_4~#PTBckbj zwHb{NgClrG|C|bzpLJsTJOguK>sT688G6QUFz9|PzKw>Mgat9<3*79x1#|P4`8M*C zf`NA)vtLuO-T1APBR21k^%{JI2S1q1n+@j%1m?cHvN$LdpXGkPo6uq9y9qd&;eqUj zMEPz4%6Aj+0(CD^oTGS^V!a}tOUA!m@eak$6VZ_#Q2ag-&(1Fu-&5qNo^+Rezn~*$ z#v)z@{n7Wbh_Flf@Mn;9mH3|eAp0$21OA@k9i>qI_Y~K8a~%xVc{`9RaIH78ezNru zu7}~;aC}ppYsX&NOtY}ZQ`d(d({H83HxZjB#Rx3| zatgX1vRa&6AJr&myB2@utLQYC@m0jR8+;YLNL#W2b~bz!{oZrLKf1U|Dc%;Zpp-&Z zYbU!LID4+F`5y-T9{(=#1s2SXej81@l4kGQXj(PR@@+JY^YQiqt_`0jAr_{vPx5() z@C(SkO6V3Nzuvl=*WvTGzgisQ$dPtj{@k+NrrcaNEqC1b@RM%j;b-y|_4#eWx-g2B z+4oxUJK*E7v~3c&ukR7YHtJY0?H|E+25T|#N#TDJh#*7ec#e8|rdi*E5BFm)-1tWj zY>;>7o1O0ps(k2RF9XXZitk>AGki>tA7TLdQJn!DhVC!i7=$r#U=;o;n6B~TJJy&P zZUHRs(4flroeO)A_e{sYO1@zjG+#|D2lF)(+Qfm;nu+%%-?K&;zd|?!>1_u+#wR_z zb9{OWVK#AKlrv&R(V)uIABX24y`7-P_cH0x-QKM`YLa4c?Itf`=*H$pvSb)q`rEejRwaI0QpSu|^K^gZY;!l4Cz954Fip;MKI299QU zAo~qbcxd2xY8M_F_9C^uE%LK z@`or?e=>;ilg=6N-bfvEd+^LWpTvsK-i`ZJaMxmoeKz<;JGL74`wZ&>`**U7dv#L5 z&*F}nT_@p=8!NgFYjy6%-IKF-;d=>dau)7BlRPl^?18Q~58)mreCr5poK3PDy%coU zfbLqM`)jU8M}BxtiXEQ`xyOyj1NrPXx~=F--45=}f#qcE49A^fGk3#`UvZb+xtjbr zjw`xR&QQUgOoZ{`;FQd`ITHnQoLzr9+cd3+{t>PZ=d7qf&!@!&EDFJ_B*uHm`mEAnstloNT_4iO6A9WFzu zy9GS=&|J4M*BzEScDy^J+HK3tb0fJAhS#1ser(>Oc{siP<#A^fjTzSwbFVLSLKLW= zulA4?@Yy$t)oacJN9WXc$8MNbLJL3M;Juvs;>S%dK~P#wbIt5Dr0lTw**B9()xlXdpaz81%>s_Q!*70=>R?(nY=Umlen#j6To2 z0%lVt7_ApUFG_2(Bm|VQ8ZFNq-flKkB&4D9{emcR$t}q&4XWz za0B7NXRr~GA&$F8;=#Eu;h#KsV!tNGfCLXNF9XAa%R7nV#j)VQuVOlQiPkH&DqgR6 zhvMgnc$Obf>{R?fy!HnC5(n;&<(~(jUI%ZQf%J`f1Dy{m3uJ-q<7a*$9ub1^39=gHf(avd-nXf;Qc) zmM;2%Z$268m3A3k`Y%4e9oP&yxYzu4V!4k@YY)LAkKO>~vDB}K*|Kg0+Na~su+%p& zNRmrd8!Yv1+L8@?2R5+O!$1p;%!4_c5LVyL5LUBqm z)3{px7lQHTTPPHwoF++LNN>Xa_b~5$#8XmUx9kaS?K{ecXXWz3sOxgi4u3B1k-W1n zzBuo)*L86}TPwImhvR@zto>TOhbO^zvza-X2s?R%GgC}gA~iDw%oaLiP8=;2JIZ|x z$b-L9M(V@6h9$u_e@J#5`Hdw`B~0+ul0XmUx`*X(EB{S3ux}np!mrSO^5Y58d>! zjWt9-lSb;^{P`JR5(dJbmkf|UzhQv3I3d8K!QIHoTt)w zbmysz20ilG$7+uJ%bMO(ik*sk72gfIV+>&b80egNWxKa#pWJzWbLJCc;|9#f&&T>5 zD4&n#9#*a~84<&t)%d-bv&I~C6m!)(FWt>`A1-)nIdgmGoq;m~(cO=N<%hU4~ZS3ka z%jp?th&lB;U4tdz%4@_rKGQk_9cE}I=<_mdjO9rdA;UZJB~#Nvj!EW(Kq43$PV}<{ zOO{_ASWZgB!*`2E-Lg(O+-BYEo||<~)eixrs{3$rF~Xn&vEexI$qX0qHQ4Nl7#_{1 z07bh=$vOBBJqADAsm(jE!%0at1C`sC(x(=rLdk<#X%3y9oJWjK!GCfgeRAkwg;(J3 z4(P!&K-A{N3h^dsJDl=O1_=$1JOT`znuC95xD^rdr_U8z$Z{V=qM_k;!|Fyzc_iOj zsUepy4|mvW_z&I6Aj!v_k6eBY#w>Sqgx8k1XVL{E(@GXu?pYrFP~;m3Vr=2a1$dzu zTWaJjM$?e#Y1sVhj=4Z+WLQH&aikTV2P+r-NQuy8;;@H7afCZAlFiuTy>zTdE_~bx zo-Gs^Nxyv07LFXpJe=*>QX{;pz&*#arAL0tkg}%%S8_*~o;%6&%Zj|kc%SfW*^%}1 zJJ+*Ct@P1|cms6UcBe%e=r^5CtkZm^Z~<-Z@MU&nJ3TLAF<^BfJj2&5^sG2`Jp2iS z+hNLhnw7o`_ID9xjqT3!YW8wEkubNwxmS2u2t|rX_sUzvFC1ZB+^bkY?(kJMDv)%; z_u-G1-ADzVhBMBFgy?kK_whH6HSAVJ{tafgnl4OZ4b!O6G(d(O%6*Asn3&5crj@iQ>*^y7t_RZ-+B^r5}=&hTkz zCH0{4J&(%t$S{WdffuhRvX~)X@NC7A=WW=2G-?pz&5e*Vbzj;dWteY;7SND1bRy#0 zj4+I)8%FCeT2bGhf;q(X)pmGD=u}vSR`v``s|kD!%o&U^Bvgq1p{xP>M~Ljv6_e6! zr$6f?kuG9=9u8ra(H}R^BYCy9Z^wi&%hCWhE9Cwta~dmmqFj!S?vi;q+}+8}zesUI znHlso)^H~Gp8D3*%xnfePvfR%ayW6%ch-W##lx4$``6tm>Kn_vnt`V}EUq&=Ka;iN zPFLSanfEjBh0Z34J2?|#FU!5y@rIy+%mxN7)WFj-3+Q`^`p(F_(1x|h`KH7z%G|@i zmucYQOt!IG?66JHfSEkw)NOHEB=G!9KACR2!za=mzTL@uir&{ceAwLKJDkkVlGt_5 zb_92azmb&LPLCU%b5TF;@OV;Yl>Rq4uZaJ?Bs2kQJ1IgkuFZR)PH0EiuOe8gn`~#w z&>FVs#_HzF#2qY6=nM~KPNX%(_G&*2VoYa9C>Q@HJcmCDw$t3x?aVir?cp}vNag{? z8==-gnViOSN7_5YH`NN$KrN$&V}(A8Y&pp%{vMw=JKb6*^T#aI z%DkDx=Q|AT3=e1WTg|=F;n3;~ACcL~xL0Xy&&a%lP4j8TmFg|d>}22t&drjO`I*CL zz1rcc%Nbsq$!lrcg=%fi{5a#5sC7x^NCqx-Qlx5^*_r%!am$>O#lFhU%wfLDC1rPb zhn>mGbleJw?hfBzXJWUZN9?EBr%IGO*+1e+Y%S*@R9ewv+fu`aQ~G;rV& z@~2v1c1bpY+Ozl^?MGKViO(vU>19;nN!+as+Dy|5b1So-lPv3GK0Wp@KHlgw^c)i6 z6Ld0ruWiPd7sdTK{D=48k9|w~gZZ7UBpM@~2Thuj31*LoeeG|o!#L*Wp4e@hjvCdTn0 z=u>iAa;sP4~>f0p}t%uBCmb6s0w%50Ti7e6El}mWnNm;!|8|ISg_Qnc} z2i#dDi(Bd>1Kvz!NsUl$C~s|PYg$~-GEWYJ~QE-9IP!L)g^=UjaG1toLgUf0B{7E3O?xaa~FUs5m$ z;0i~apIlW^)zX3>ybcHQKnoj>%Rxl9IV1y65~VJj+^Y;9u9! z+}=_prWy|T#Ak9N+ zK)brRsTDVCfAAF*CDlwAQBm^Brglx7H(X13XVe(Vk^!DAY`269S=Ylpk(o zv>#DQP}1tPW>aGci&7%B(#KpdTTa@nqN!<79m-mceUE6*`X+#vg_^R4hBE0!Fj=@) zv%M83Bw`C8GKO+UtFe2aKUHeW$aUcw(proRvCL>Pubgt*H#nULBfV^ zB`%Z4J@&W;$SP?*8@0-(*h@$rMX7l{C8pkFm_T3|2}w=zX~7_s$df+B{(5nQ!zGMQF9R%qW2l9s~KE2y|(OVABDBO%Pd0zpC zAiep4{H4Lqbe#PfDnd;+!AWJ5@e3WdtXQ&HH0L zj_74KEGT&45oMNl@DF)oz8Hi_#)MLk35p_qHWFc(2E3>BxI@nZV*a?N^plDlH)!W` zOuSl=;|%R975QaPJ7=DV8x_B$_`Kqe6<=5ER{V`(7)`5ah_tOVyof`#d{UMsQ8rP zR>hYUyA(rs88Y2`#c7H>GnnqH6uTAQQ{({5HFhDz5k!O^&i)K{z8AUrDvJm^ifLgv zj*lFB=nUw<(vY@=}JA>oE3#T_OA?rd#Jc0 z2k*^u?etr)cSQXQxHfKX*C5F9k`NZzPoYeYYvGb&+xBw(TL|*K{RQ5cSmIaQvFkVe z?J4=EobPaQzVlOES?EWhB2U^aK$oJFFR=RW)Sg1WkM&#(cv`Q;fYaDd(f81E(Nl4^ zJO7}faI_zR!AJl<3Ga|60o+StQiwx4B!Dw8(j}<`@au?WB!I3b0dzeH;N9?X44sX@G`_2;f#yO8_JgeE=Hv}6LP$AMCy@u z$Cm)kWjekDa0v6@O918lG|`Kf5qX$Yd4+r|}(M0?52LA6x?HR7KunOkV;> znUK?40+_}i2?-z>`2~(h0G*%&(E0!DeG7bDMYaBZoFu172%$}<53Ll)myf>5B># zLYrcnrjWKN6fES?ByDNhge2u9LgYgbN~GPZDyi zh}VkfMFsl*zO~ovy>ptjX?e)yGWnf+v*xk(%z`hb#efaR1+162Lap*ortm%e|J3 z8%O{@%9b!9zew&!8EeRBZa$wNLuTfVV&XsoIGSw}NC2CeIFJC=GjV?jpwnLh=mZkL zG$!sZ0dxWhAQcasKmvFJuNz1JsjB7#62LxIA&>z6l(B&XkWZ!)k^t^ubdm(n2}uAS zB)239ptG+8u$pW_5oFE?@-z-2NFQ&{9}OkC4m3I zeElVW&H*HVPq7jz0dyb%Jeu4B31BX{1rosTlk6`6bOH$=_lY`z1h9&?CXfIQVcz}{ zKqrs@%11#U0i4LZ{Uv}-k_6BRNdPY-?~nxW0+u8s0sJWALlVH(_%aDe059X^LK46y znEyZ$K&oC0Rs!fi0+_`D1QNhaBo9>rh^~4ppH=-WgDgGLyLrt^h?@Kf~SoiG5QMMGoLK7zsJMf1M9K0YU2 zGT*&)oo-3kXD@Ib?u&7}N99>L*W1@Po7|0&4JmW7hI2;idC@M&+hPdgx*gu_zPxCR(s=ueyI0Ww)6yl&4fmIhasrcw`Iv5VD8P7UCZ(H# zcN^b)#+h`E4b!8K^sgKLhgFJ6}W*vkDF1T&`^CWtrQWVzw)kX(*@48QtN9(b(> zAxC4~j5F<=EH~T}lFL!rWB4V@4QW$%lwk%mI}d2phL}SMZu42v*dRmxnU9beZ^D~X~~bj+Z6%geht~sbVC5mb?<>6eX7+&4Z~XP zVC06SNH-X{;Zn*2aWTpbkHn8fWxZHe)0g43u0wyBOpiKKK0_M}hrb(gLo{#5g|E)? zv*!fv?T{0)ZRj{wP)^A4!8pc^HUyA#VG`YMvJA;#XiUcxO?0?CjVs6xS2wPfjY-47 zBM;2jKPW@2BI5owYq-b^AeUjkb|A0db$!_a#slW(J|&qO1IH^pMe!8HnTjGyL^_cr z0#|7G21Su2B3xvNz`HeEWQm|3Qd(q*phcDl?9=c+Dn@a;SgxZLMV1I!WQjnLB?3j3 z2)t6$i7XMc$P$5fYq+dpf)-gKP-KZfktG5}mIxGCB2Z+BK#?T^MV1H@St3wmi9lJY z1&S;YD6&ML$P$4fO9YB65h$`mpvV$|Bk&-xp0ZX86j>rrWQo99jTc!WXptoXMV1JZ zWdoqd5`iL11d1#XD6&ML$P$4fO9YB65h$`mpvV$|B1;5{EDdv>b>*) zeSfqs?VXXnsF(6X3{&ZL_US$7`Y)L?(@CQW>9%!I@88AtTz%qgJU@LP8QDQjPyaq! z5iUe_7Hr0YoV>y@ZCx`&;k&18E4Y& zquc$K7hb$d_E(tpb)FA>UBOp49=t7z10I$xMe&(Z3-{euN_`(#hr@OfbS3sv&dcaH z&kFZ2Z)o(sTbRzwvzwVXmXP!^27VU=f`hBlLubw z3Ovj)deoWrI}dc2Uj);A&V9*`;Y>&V{1y!JyA}Kf`i5GCbYXru;AhH*&tRY5`Xqik zlgekVm27_^FH}B$x0v$Xr2OJQy63@>OJ3CnMu z3Xj>rbx;3wHkvsG@SD-Rjy&B_^S)7?ufzC`W53}%@FYb(-=t?NRw^!12^ zHsIno19Dea_g>5G5!=RPu(^i0rIo;$jj(%6+sIj<_r~0wtT^mWK%WhBF4%JWGvXKZ z{sZ$*JN|l~$^U20N5PM_oQ>@^EBk4rT_AJSa9W%XIpQzSWwWxM?8CY1FCEZ!MPRNX zVvI(lc*Y=7i|v7?tZDuAxROpC8kN>uo8lE)dH=H3m!#MS;A6O8fAw(o;GG>s=U zdj9GtCVeML@B+(PhiJ!Jb$DBarF`C`1Ii53pH&vv@xnOFpLNYyy(wPeUeZ*F!+toy z#3Qg@J7}!VzV?T|i%^dCdLj4*o@bhy!T4Z)xt|%~Tb*xemEV<#<)IiJX1P9?S{}w; zr-9~`;U47;h5ODcA7OV3IrF|vo>%TKT_N(CJY>UktLaf^;`rV%enypo;FDpTi8~nq z;dxd$h|eI5^R^WHSPxDylIN8#g8|=7#ui9cW(Jfp^+C!wksl(((Nm z=Jz6qY=pgC^U7C4<$F8km7GVL@*Qkm`6erSUfw3puuHDp=YBR2? z7jwc9I4u;sg1rw`{L_8hN0Aa=pRnTB^x^!wmp*oC7tSSLkG0XN?l|^gM9R8)vDoD385 zRwG=esvC72VIyB8wh>`vJs&+S(d(8iOxR^z!?JL_zP)=NZu5PTRrRPoGfpgHe_1Uo(eiyZdO50>w+hN=7355Mp!tkAl zb=}S$<|EqiDnE{Ey=beK#(+2H78Z`Z517fXA4kUlfM zy)S~W2(FWj_H5Pi+Huaw7xiKCK=YJy4wxHv`vTnoJ;nhCjpUUkWs3c^&?^-m^K1or-q?A5eb|u3d?8Ey0h^qg_^x zzUTGHG3XbQ*sruduD~;ge%Z4mcSoPieM)#%@oaTt{;_j-)uz5$#HYvjtZna$W^d^s zzi4a=_}#Rd-;GQ=i~ZKx)R!6C(pRngGQsbUZwCA@@7b9RewBmZhld5rsLug7OT9qs!CoQivLo^qMr<+U2a9Y4A*<`l=96+9ey^5yjzer$GWBO;w+Anojq5ey`CG5`on)y z_QQ!?B^dMZo|tu1ed6k)>JnCVUBbos1NPnh(z3c9-dNh07#>5LA`RXj2{&GsNQ*D( znP^!Ryyw!F&~EplTnB5T8&LmnSH>U^X$-cUWWxB2Op~P+R#}jsJUE+tQ zb#>1yU6!zRT_@#S4}ayT`h=amEU^yAvRN_EQnp2j=lAYC&(2;aJeZd}n4kHWhka*w z_NKlk2GMQ}DC@&EuB;)@cv z_p?#OH}aC$`BZn&1K40*p6D%Y>}?v?Q~8}Xc6b>N42ljrczXX3Z5VD3#kA326!8Da{7 zH)|3M_2MvuOFn+%aIVOeD@yTV_!Zzc4!=VDIA8VVZ{E6i8R$y<)#>T8dSQ&)FmU zGnWHw0#2}3yFUxELo-k+@*H+NJcetIIWakR3fOa7^U>i8ko_^iK2zM3fba~@9&1eT zjxeTpj|-BJUtU0-mMN!JOo-rPLYm)O@4NyN8aj3w`=|J4);qrsKUO#~Ky%siLI9RM zHSl5dI;?jd1wazo3-#AKe=Skr_0E$So#xIVhcx$6fSsC}jX>B}DsV=p{SV{wMqGWh#@pwlb9FUb7|p$e@_0b(j9tdv@Ef}1rAMAiwM!g37C9S^(+u2g7Z?RI@pJCXySma~2;-CG1coPi$4!zdV zywY~kUobiE3Ub~h$t9=kaJ7zZK=QMh{1%h^Q$g}i1<5~6uQhxF{<&k>^tOHM)yPZl zh!5jmnspr2?(A_FAvDG9K+v&wBqiW_pFQ~|Nx;r>mDyoQ+>lv_o9Fo~72u0bXCX>;cjwMgp+aqG& zdc)ncXwIqN+*_1Tsf&LdZYWiU9sN1?aB)GcXrb} zcgL0MR?Mv|aUZdrf7o`s^b_s_S2is#0;gET`bNMuyV!mD$_fc8@`CG?m2 z=zqXpRI;L^(!BxH>l*V4QvJWHD%`(XO{MPjR|$vZ^H;hLTNU$)N=n=_E8NGeCiirY z&y)%`tHPZbogo)3Dt2d8py(jn?_F6Oy>!J6_s4dHdt!xqHq*OzU0J-L5K^1MctfR! zCG(ywa$iQ>Q0V3EH?Q*8K57-a=SYsvBggU*o_%v07=5^@S&Mv3vHN|gp8FUI|In4Z zX8caKL=Y!}7P~)`=xI)#$b_2lnAc&^yA{8Rx;54rjrG?2x;E?d);8c}R>j&i_%&On zuU&2}YHYR6TGnBm*>bT}4&#DIzqSr0^^m7cJm`+dyKDsz`Bydrksm#Q*vw@!@>vUG zi{#bOxVpZPYii)xLLc~=AoBP9i%^F7FmG4Aw$VDLvB9cdvDP}HZKbscCjcX^0mWpz zAkwtcgLG>=ALdCb*0xC?u7!M(4tdwMi4V5X=AJxe{>ft&#m2N+W9E2%mR0=3b59<# z`s6VU61KWzOhd^SsS6+Ju)Y@OZpQH~f<1hdS;K>Ed>paq7{@`YDl7APd!w^~&#}qN z_d*9o)$*+DIa#sm?M==`cLSdGLZrc?1IJZozG}D*Do-hmcGEy$V_lnPWs#}K&r6S3 zb!G}dvH-tw{2K6Md5jwe&U_8uk6$>wc_nbf=eme-xUiU)JalPX)7s|dnoD5E5O<+{ zf5s;z(dw2p9V)vsikc-`i8HAR##tN<1s;T;W5n> z9D78ThbNchE#NLhxfjrU*jL-EX=Ph`2W)*_W-)Cm)Ekk!UFD?x^wLl|&thnMV}~?N z&6>4*Mrzve1k|m@GYYGdFfQ3DH=DJfMN>d9FRO>es8?z6lyI`9wmQ5kLws7-)~`Vj zt`zXDgDubH4Dv4Cp90UfZZXmtaPL?688#yU<*oWPA&+rnq_-Z*@d=N;FQ*)@w9GT$ zzYm%22O4MijX^+|-y``LBfyCKn3w4|WRxfxehv8W2|He!ZVGPLRO zj^S6Yc?^rPp<)iMNX}_<*Z}PxvO$Wa@^r$m&KLAa?DSIc<@thCi7{}jZiYEMvSSX%uh)^6B z9ocVNO%xZD2O|&Oi*&svQ-~k09V(9XDjvXA1RRU-7=ElT>(4eZ&j!3!11{P{k2>-h z$c4iXu4fp&0tP|eKE{? z?p@q;S81(mCivt^x(aa-|YJg?;~sxXpDWrIL6FxPVVXM_1Ls0R-~uJt zZ;nFyuL`22AGIaXSAsV4O3Kr(Ns7n%yzkf#*|x@!_Fv*u>}>ERBEgg@G)3z-t>{0$ z>o+MVsIx+oqVV(uZ$hro0M;yn!SH1`Ul@LGo?BPn(uRp2EyXv}$OFG3WzJWCBIb?q zp5?sJmc@HVIsA)?yA`Qt!|*+duPVN#DEsFS&xe)iB8pMPOvP+P9;?E1!xi%sV~Pce z;}i=O2_8XDF5_mMhLttW>O0tX5p2xKy!Lu|bh1PVqX;iX3}LQ%{I^sp1ui zor)V2H!EJJxK;6H#m^|-s@SD?r{dj;_bT3}c)#KUiVrI8QhY@5QN_mJj~rzDBi00xFR;aN&I!h ze8@DEj^UNYd`lH~E8dJ(8^a%0d|j~tlU0U4q8Ps1l+JbU|64vqh~;s#B3hthW`azUHv zA69%?@wXb^t0?b7q!R;~z)=_zc)dA9{3;d2s380`N?)Pasdxhs`EFPG-xQO_{U;fY z>u`S%Q7rudWt;|z-AG^to*l;LD2`N|pm?(4EX6Yw&sA(xT&H+F5x>tX{Z*x9+`)C8 zP`Xd!|EzRs#4E>8BEn>UI?@-Aj(PG_wo?x8D<=8FiJ+$_JzZ&e&JbU%wDe>AGnlR6 znDSszwausp9`qbj>}QS-cAv@JC^MX|AINh|shek2E$E?cpamU06nzASs+ZvJrtU~e zcA&RH9k+<(**!M8b)qMTw68(lez>yZLGKX>Spwc?j|d-Nx>5Mq_v4?DnevVVdqgfJ z=GFnYN93POFu~s=!s~O7h-G^+rm8duBt;Tx zFOF_xDM%iH*ZbVIw|x#>d9ZYH4U()Uvj1 zS!3H}F`OO~n-q^vy=Zb#>3HjO3^G<-V_iME5!Py~6hxm?T|R%VmFnOFueklP)g5*9 zKzt(!t}s3>M7}TfAY-Z0!fVQ3rQnzv?p@w+=IvF+nB}`UAUK}p;K%$-7sU_LI-xO) zi|bT0z_fG|@niafrQ@8!X?laV-c4^UDSkQ@%{ZneuTB!{i~4Ht>tlqt3*w2OZ`&JXF3c z#2J3Kz|TCqe{?s2pKF+arkB6l6#?Qtg$}{@xp5}TJqSQls|o)Zh=JMn%>}XJNTO7zUShY zauDAM`&PWybQ~+V28N|t-?81yD~wBAvd4r&HUkN81^CfcRhgp9(NerM&G*vYlP{xu z4jz;XiRdtVt_Qi7xxKN~pJE<-jyZM8l)=t1TfOelZ;lyB6!pjCV=d+Xw@2XzU7hk;c6oM${?o;5d--zDaxur?@H~iy$S{MLQV&TqUYV1ZVrB? z>6O-MmkFS(ZvQI<=Z7yer^H=^>W3p8Mns4h{@VM>=_~ zxCCKgXWpU7bH)9oizBniLq1GbOOHAeHxsn+Gm7IDpDN=_T=HCTIO=gX5*=)=*Z~HY z7-m4jFL|!GE7UG*57RF7p*&@U3{N1hy5XXZJrW*oivLw$H zZ$Y}j%oVwQKA5>;0_)m?nJaRi9KWxOGvzb1!EpGyF;`^Y2weE;@625B2)$SVkaNZG zJC6M|ct?6uMZVTxxY|3y8nZ>F=Unk$JxAP#a&RuVm54riv*PD8oMQvyS86!RJ=nQo zYhYgS;B&=k)20u0u2_aSHJm?J>}U6ed)Tn9i2mT)yRk8c#ABMicbBnuvlM%HxrgtS zKJLMzy&Gs!Bz)U8@h2r5>y}8fNBl1%&0n!r$-TMUr+m0;mf>UE(gt3@9KbGPl-pzG z%B~XY-+2eT29R#vDt@f=18sgPni**${FsrR1Hg>*N2JmWbb-Q*^d&D4|1l$VQBeej z?J_>ah&1;r0L(@Cj5*Ixvm(uY9sjY4N%0093qiY#uyixFmj0?JE!}wj1w*j9IZ=YE zhhWlJC|;~a{01o<>+AggwoXv@vlMrj=X2j z-nL^Afa$?O(-k6($wMCGcR4-kOdRK`#?Pn|K=A1>&cvOJfbg8`RuEkXljUz9>%nrJ ziJ!>>ul0Kn-Snt4aTTD${AOU_V}A0(G|A`3xvI$nuWY3nM~^zwe&>M>^D76xY=n^? z!%g`(R}J%{?#V!N+EtnlAg(%8z6^vIe(T{6^E(PRFeW*?e z_?ah;A05wt9BBP>JK_v)rker=1I=mgLL6B5U9K7F1~aF<7U{l4#_G(qh~Asgi=ZuO z7KC$_1Fe5@^$oX%~$!%aNgOh$nQPU94CmI6mKM=sXwLo1I0s`r*;Hl z^MlV*Pl^wEo=O`XaQ)_~#tt~zafGGwOpI2;WGz$Ynz)ncu(#No8GG_~u$146I6E`` z1P%mT2sc;vPRG9F@C5beyais(rO(!i zAL{u)2{02>-djviIgDb0`Z%dH17Bd`sLM4@1fBuP-#en zG?kNId!oN`T1)c$=&uOln)tse+7l29hH&`i5=%Oyr&BpD+Dq~8dHntt%u~^i_dida zXjjBb=DE4W?nONiIvZCoup!Q7PJ1J~lmxhdA)=muJ+t(6$> z-&>xFO0D*p;c5x~l~YvgrCYY5u@S4pEvvAbOlRSPQ5E=Lvf2SjLjBqfY<4;XH_&{w zHk6<96uMFPaiB48+?yE~axw^kPM)vsFI^$hnmlB~bnEF+XX29QtH&UMPmFOU-N^_D zzrF7Su?u0G5Av14dT{D8(0uhj5pJB}SAl>qKkkKMe)2oee3d%WF{Clhv>)HSVSdfv z$Jqk;G2E0ddA>>=>cPxcuLzazK=aiU+`t&FXPl{T@_cnesC);SuZ{t~9K!@m>E$nZ zzREMe2Qy#22c==aUg16>LmOiN;0`j*CEmkc(U3PJzot2gvmnV;QV z34b}_tgW;~NFC9x?($mNFZ++aBCNr>uuC`}`lB&)TIwbr=qIyFv{4ujl>SiS$aApi zX~T(WBly{!UA>i^sAFP#pB;a*kL$|ii7kD@G0$HL`(x!6+6Hm7zg})_&n?WR|N30o zP)Du@(-)m^6aCxW0Wl^4Gx*B-%`VM zh)*euZ%KIuYu+pa%UWKG`!zs$e@Po!mSU2j&#NFSG8{G`v$+WYP))8gKk8-%|U%uLH+PPPA>xeYhXM+vxm0$&<-2pm?-o582U%BpWCs&NqaI3@`m(Mq>Uo2 zmFQYOC$VK?0n^83q7AqA&56O@XD6@qS|7{LI^z8+ZIy+#ngf2UXKLvtX&0tLdwFev z^mEW2ub`gta0dT6>1A0?JgKYqmKp1NQe$2px785Zi1-I$nz`%FEYsu;p{u^%t&O^bCw9p;?gw9c-tWp{Rc9y@7z?bR$BKZt4SVgh(?r8)-Gp22ZOCzeHCr`$#OYrH_4T4$4crt}(QCvtvDfH0&(?JMhTF z@*dXb==k+LHteioZD?n~=_C8tuGgclu`C(57VN-vr`E1X@cNrh-;glZcdeUYHW79; z^}12CHLsNxUy*RjIup>=@5R0cTqmce9QXgovZs4Lf&MZ7$m(8vPCg&QwMSE*DgKGR z8+3f+IPkYO_ok0S+z_o7u>dr$MSot%PaY>_dCq@1mt-&c0&(slL{}~Lkst?9pU9O@9Q2X zb*`p;)86MgcRYh<>G>x*cl1ST`98|&wW~JvJW|GfWu=?@?^?I?yVuw$+#7{mV?1Av zZ7kiKnEI`G(sxbza2=e}f7)A6(bYY2GVBuq)2Gp{adr2VxDV;mX7vnF+WA^yz5T6u z>#VNC`l;Xg@B11X@ivIs;%A9W6IM2yOq2bGp#LNpTHbv9P%)XX*rf+`KY%6`RDA~ z{>?nZ)mjLv#duiT{B^6W>l=GvbJ~hsok-o)bvu4viJJtCua7@vx%_~cC))8 zWp&sBc|rHA3$FgUTeb1))2eFkNJSf_lx;|))NV?oq8~*PU0+L0blskYbZJ12-D$0B zc45rg6)9V@i(@dphn~+wyK?L>(qeKp#Ljo~x~LPiAR7Lqq+zTJM=jZ~E#qq<XmrzCnEU@FE2uJv*(DBHXD zv(xZwPC-AiCA_K|@ zseNCaYFPIL({q?;g{{H&jm&_dS{5SSqD|2N$e|%S4cwch9 zJPiEn2H zKN)90;b*7L>WS@4bZ6kl{`6~J2YEOyVX-%TZvpyeJR`w-cjSpPdmPN2)=68B=zZdi zz3J64rd`kg+XzPmJyWZHl zlk@FI9~#+r{SPY>hF7L_t33CgLs{dr1zgt9!+BR4%Jr(Gy>6ehX5PkmSw5bh@|k!? zW6auscRJtioF7bCKT_rgGuDrMOY;NHZ?a)8-L=;Fb4s>EJhBs8aFzht596q7-6rkuhyC!g?!$LpSC{)c@PaMpm%@GJdh~Jj zq3eK`Bd&1D$iB{`x!Fv#uW55$+q@I$H|=>A(%9DJz2GsTU%&d+`X0{DKZ1Db^be2Y z%!I@?eEahohi8$Y{Moqg(CG(18N;~0lxI7H({UewNh{Z_3iTcE;d6uj*PVj?$Ni_~ z+kyK9!uw9~{Uz5ppbl49AQmaWrhtr$Q!}tWtKlhg@dcB53=9AdV7nO0uf~|~5M#_Y zotBPG+KeOoT?W(Bu{g!k&!8VF#*lF#&lqwR5~*B`iHw5P`%d*+ygjp1E9FDN6{ ze)qSj;sk7}2uTP>_7@n&0=vev1TZRH+vX@6N6Ld>&J+^)8Y4tO=wd0X_fhsxyA1iq z=*3W9vO7_^D>*hGJ+sSVX4p!>JPEH=I4S_jq2=$+t58NosVpP!Y@JE_XMdGH~}5%&Ocm_{70667H2C zoM9AmMvlc5BWXvRh9W(Q-*9Jq#`%n$z!0#^s##?@6FvTs3^`_9sc6Q-Orlg~Ms0}7 z&Z@c8atdb)j~wgBNCMZ2E(ZB?c%(+?&k4|f#;?FB&A>;6NwKM?gY(4@3-2{Vy|V-W)KTG$eJb%YUKut%&op+_8vsPIAZ<-6i*48#u%Hm{d2C%d9>b z&HYI@HZ%7tyoIM~-t63b@;J@88Oz8{eopQ-<~?0wXQspII{8y_UtvR*YwYyg zr^)IJ=RV0hBX=2N=Q`dHRF>Pw>sDyu^4yQ}(VeHUb8^#}x6=8R_^MRP058ExnOkqPr{hLMdJO({?^ zHTN(|$SW;%u9lK4$-SMi=R16L zIr%GcTgd7HC0ld9&)^y*FU|cGuUqTj)E+c-r=5EadDS^sRRq1+&fUcLdbzTj-(~0C z#*!?P>~8*@cCJXw8kD}*&fUWJMy2n!b6a@1CZ!*=bFU=5T$i=OQQfQ5LwxAFt^;kZuxJ|!ABOYtB$u4quO)GjQ{`(~vZjIp6V-MrwjZVYJBO`o*MzHtV zW{ml{gg=P?(HHTLearNVi;)=W{SjOb|Bpk}LG^;0o$5Y-xU^GPzT+l9yxfO^M^nr5 z8m!dWWRthjNKN7~3~|9oD1&+7 z7mU2nco&acW!bG#$|WP$G#LLm@S|=L;okU!HF6Eko$!(^9=YanB(s;s#-R;O zp2txT%f5(jZ}-MWt&!L}(dM<(BOs7Tsa>E@V4>^F`zOv7?6XHk_%gxZjEWXiu}4=i zWv?S$n%%ity7U<7#ohACvloqFC+>FS30P2w>z9~ zx8^%h&IWneky}^2ErPpejg?9ENLL;mJ9(?NE}n6j*{TegBmzo`UCRSQwMY-?;>)8QRgQA3-f?Vi86W!Wmv z=kFat`>t+nYg~q4G_s!<_X+a~+)F(5f3$F1!;uw*^XA6gi;A(mXui{wJKw$9zRi8o zt(b=sAS!XF#dki|wDEfzO6C?$b?=W9yN`dYm{@|?dG6;w*5r;`x1gk?u%zU{#*(?E zb4w<;m+dV1uA9+RRN{^)E}2^rubeYEes#%e_vi}u!s60raYgqc=!h1TI)%d~oiS;B zQG8*Qn_lc*TtbCbzx;? zrJGaXwiH*A;Yr2r*PP<%w>n~NtSL2(g16DGS^*to^^#s4^J ziliVB9bYkTQejDHV$YWm*%d8?mwJDPaJNZ~x3 zVsf`r;cmNXo%^Mazv6!Ts-_hs^9tQ(aFWPN_Ri>NlmLg%Y^ZdXR;+L*?wr5s)j5@O zD&0+2g8ZTN3fk>2l_lCj&s5HxH+Ok_N71}_C|7z#iMzJgeG{CvT=^gFP0mhtK{R&e zC)_9OijukW<`pd{nY(<)yrRnG4W8Ksv3VTct2U0kQ5}c9@~ndLtgZI-_9vZd-A_a| zr(Bb|DQ#nP11AJIp>)g#8xPl`e~Vo^MsfISkZ?WEqB<*k5@_l1))0(9XMl~jLAC+k zro%B9c@LPyAy~XkWHg|{v01KRd=2-&VXv(`Wi=g*Yuc@vrZshK%YiVd-!NsmRnxe- zv2A(dnq`++jXWgcFzh?j)M5YE%8s~IbICFc1FP#%bOe!X7rI>o0uAMUn!)%$ZSo4b zx*q8-Sr+6EbF0xq6ap?ZM-e{Q0`I~mj6N2=n{mXIceqClv&oSg*m8`(mNhjjY>n3l z%b>5kvQe0DOY-t&9GRl+E0^IFYF4gpUEAiFPOfQcu4CijT#03u)}e+CY{|y)9M=6jMJ`7uK5%g<%n=Q{eu9WzAY#dGaJ| zU&JlMO+lK5#m97+{eRhmZAdT-=2f^(ahp3*O2s z6Aea7<@%>DwqWuxfDsPug>v1x<{GJ;S9@F;*FixVFR^Mk)LS*H(I}x_ zTysg=N_Gm~4>?o=$4S(!nq;l0ySTBYVdZkfXg6KC<}&H34Q(y0H4QD7p!#bXFYRcu zS}ty)C2KI?$sF44w7F~6qSOeiX>VAGc3jzEU4n)}12#3c;B1o`c8ungI7)}Nf!&jB zTY1*3c{K}X&sw~2(cH6V*HrUf)-_o5_4O+|+O0ay3vpf!E(*IU9d$v&irG7q8KuM9 zuO`%7a&x4LJoW>nkv6J1V{YYaQXcZrlPa+=p&o$ODwtscU9)U$TN~b@Y{@!yrH1yF znia@JKW=3sP7=Z%`qg!pY6nDGG<72m`$2TeT2eLWis;P@#m!)m8a>iYg#O+RZV^KDjhZ0&g_wTU+Pybm)o+YZVjIT@22PtUR5I&0?~QpjgNcNz@jX! zY=MoOn&pih=va+4(t}OamLX($3ywo#Gne9-U9+~m5yKs7iR|smc=(Ig!$KXdA(@$}G@IDSZ#?ej0kLeGVE{-%N54;xnWq2=hl78IGke}iI zghngh_Qn|=ry?L+j~FV<{d1gGunkN-7Q%1xz-vWfmh}%)*ihV=@Q3-W_^?+m@?-rB zzxq%fc*Vw4t9ZPBOhe-zag5{dc107u zaKgZQBJM!~G{4_u*CO2sCR2z94O4_}lXW-J?HnK-uN~?}*3(#sp^atep;3WE>cM)k zu3>(6VlsIVGph@EO^{$X{NQ^c!dKw+*ouK0j19x|AUS-t?6Ix%!=RW4zbA5JpchYD z`JVIQX%pYjd~`aoRBLGF|4fKx{8o_)F@=|ZPl#rG6&p~T7tgu5p&5UFh&D0Acf#hP ze<|>%pW6wIBR$>_K+@Ba=vhfL#|Pt>zQzzh(rc3Fk0;UBCDH#ciN0HDeh&>r`+hws zo@?L6@p=y%0!aF?B>ERg^ly{s7nA6hljy%B(XS=ZZzR!ap$CBFIVy=hHi;gUM03r} zIOZ=&qRT+DZN?#v?emeO_(e%{4QTUi$n;G~@y$teunDjjN+|| zU5a-q@>ylRdlm0fykGGF#RnC4DL$h3sN&;_PbfaE_?+SkiV4LR6?ZGXthh(qgmV;-ZI1@0}WB6!DJfmK|Y+^p<8bp+1;lZ%B}l~k zKZ=(j;|qwO%M@uJB@dE~R^k*KTTO)K>#f8X=G8=$GY{`cmU|o#^b{h>eK*j-i|1Y< z+Tk^ZqrR^bQQz!LASUcwsNxw@&BOwfQ|Sm^stkv+l<;jJg72L~l=D6!%9V$S8m}KG zf?qiilc`SP@wmPsjhV1~am7j^(sdBQce94ys^NQx;QPA9S7t+0H^!R>IMb02G|#T- zB%XqHA$|zgBhK*V15AhRaqzvA$ACA}0mmz%YC?mx;7r9c70*{(p}0ZulZu~Jyj$_x ziVrFNLh*UUKE*#OM)_Q!JVz@QD4wTSuh^=1rQ&sppH;kD@mq=zA`9QA6#EqaTT#vm z!*%&x#OsexJXP@{ii;IRD+1|6hXMF`4gZqjcNBlFD9Q~;m&tL0{1vAumMbn)tW{j2 zC}%k#-~CEIq$o;Y2!B~=eqWJ4zblEyD4weLgrZ3F5id#=!2i+kRE#&wm#bKyI9YL~ z;sQmETTI`gc%|YN#ak7>q_|V@CyLK0?pAzFF^UdMzQYwu6i-v+c*yv36q^(`Dc+!X zo8p%hf2Q~w#g`TTshG}j6MTAVg%zS zuRBF?mLjE`4BxECjiIDpR{V?NKNM|@uZ*Y7TjD8-a@G~-b4a64epK-n8vi%NqcFZQ z-&Kk~S4=3*#yHCOPbq$0@ry+8{ifnh4d12okCgtY(oZP;tkN$i-K+F&rT?V#t4jY< zX$nACjx-{!dxFvvlrC00Tf^&>Zc^N=;kPR8RD4eHZ;DrOd;tF(j!*bEoCvy1>2jr) zD7{o^bSDd<8gCp1VtAH(GN)TL_&whz#RqT0sA?!Kf>$udaL($v=bJj*dDP90ZR?{> z_EPMbf*yJ=b+W^@*_q=59qYr;u@0#=XM|Oo^ZM&i9}hBQkm-bE)n+T74!Fnq*n82=JvIkMSF1aD6bwF6B*}gH4kGM ze68jqSq5LL`L9fQl9w+hqZ&;gXf^+d`FyQrQG)BQ)w~{=MXQ;g%7IohjhKYAnt#s7 zK&$z~4K zXf;2}O9fiZpF^5M&}!aHmcCZ=L*%s|t!8`~gtVIPWx;%{=2@hCt>$e^>1#FHyn?UQ zypt&pL#tV|qJ6FAK3-uzTFpG_GSF&nWlCSG`BB!-*J{3rRG`%??~{XTHUAJbcyDPn zx1q+O)jXGt8)!9ulr0fxHOoBPkkQ;8CN^Yd?kFY>w3?q{y9HX!6Zv2STFv!L9B4Ho zEgmt|hMk^!4ig7j&1p;=Xf-!6aiG;qRV*jaYM#w|5@#8RiSLny)9<186nh$V!B?nrUXh3ACDj#O#4qb0?VxTFs|2 zHqdJRG4lpm&0JUsX*GX`i36?XVI%{sX8B4Aw3;U}Z=lsYhKXwft>#NfhqRhwELTXY z`FOTTNUJ%6@gc3|yO}?v)hxQ-A+2UPfATKVShI*;DU?yAGkR`k^p6P6vH6JPE`p-Q@st{A^%jAQFV|Ese989!rj zpQy3weXU4ZtYMk?sma^#0J)H2i?3JFPkBVNn|!qhRmbr)t3;8+S9SB2R(GlLQ!LkwEjzs3ob=#=dGfo<>rQ z>e2L96Z6hI>aS!XS{Kw^5!IYPJu1)w^3<%1R*Y!F$UjlwF*DY)o~8}RHZ=E#c0N`4DF4c5}2p*=4*I)sy(9IL@gXB`$z|= z_tb!-$bMS3KtBRq&%3q#bms<85A!wNRIN@m@I2KsuLwzcYyFgdjE)=oimKMJN}?>L z>On@Si)uBf0%AqC28uUfO}c$ljEuhCz7-8?Qw4fqs%3Y$T0!O*(~cGnGQOHtf9;@T zzR5igx}9R-c%Cl$dA9<6#?SPpr8a1 zm+#DV??DLq3cs$!U5a!k7y@Y0CF?icf`FX^q+@!_hJCjSicN6@0;IiJRJJ?oYWgU= z)}EuWmyaHG=DLP97!Lne={E)aD_k1Zhdn89uFN9CH1A~KTxp3g%~lSa3k?&t64|^R{GnD4=IWa6!9Vh z1_fg%G1iVPGeGEkt%K!M*pulBlLzYiupuo>7EizEhA_E1A3=}9bP@u>_ zfg%G1iVPGeGEkt%K!LS*sK{Srpg@s<0!0Q2l&^cB$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1&Rz5C^Ar>$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1&Rz5C^Ar>$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1&Rz5C^Ar>$UuQ20|kl<6eu!KpvXXhA_E1A z3=}9bP@u>_fg%G1iVPGeGEkt%K!G9y1-=yZ`o&*~=yQKp?$>j0^Z@(2NV-_)_DaFj87NIX-<_JU;0|vDj4p)BmOM__S$LVzJ4k{7-jqd(54@ z7^=vzg(H+_kGZOM4{Rq{Ww7yB3%g3KUEO2R{E*Le+f~1Oaj%uq1I>?KJMr|3?k>;P zr5!6r*ah7fJJW6Jg%=qQ8%ZN*PZ0K&Y%8%T1tW91T~*%0d^Yl-8D7$Kl81Iyy*#ju zR)#$Bu3>h(Jl87Q)R$s)b?+(j>=dJC^}=Q_h5^`XLY&=NF6Zf4WjA5SqTRqQ8}@}q z(0(n-Oqt?gpS8C?FcEaBvJ!(zm9)AsIM8AA|X2%o#uWF zfD=%L;Hoy?5cY}w(I=0SJUrvcj6JG}{})8rkL^*C@$4vHBKD|J{D01n zhX`XO&(d^kb{!|-7o@lQ{;G6*^jY?JNwZk2>)0p7kPT)A7?x@Cy^rNN5-bPX!z;`A zlBdcm%lYB5oG)cr=*`p|nV-JBt8@D5$D=7bc>QIriMD%radGpSxh2czx<@p{i;5;q zEx~R|_tJ{xCD__I8Cx-{3QMq8c3zP?t)dvCfxYi~7?-w;W0mJ*!H<7{#_MmAvj z;Lm&*hoQ!rfS=c8=HupF1qv$==Csfw5#$qy8gLJ!>12!#7PtItvS)}JmfE?o3kPP) zx`4lM<9iN$QN~|4k=2fXtslP6;>rNaDBem*Q04a4x;3o>Y}spHwX(Gad%^JWj-6<4 z%gNv9ywV9i_>@1qPkA{H7(Gk@zLHHnFnCG5 z0OC?np?@<3(D36o0KfN)%gYSfh3#SZ)x&S{FzvnIS4NLI)6U7?@&(`*LmHNkBeCI^ z{4LM9ZZYDGGuKM~mM;&L4_=>N^0)j5TqtIk0S&+P@Q3SL2_~E`v3!_k`23Q;B^H9B7I_^u}y2at|ssm@~9#wJm=DexV@o zn(L5wH@@Y0F9R38`aAROexzRPaUj3lG2xWy0sA~@|DA_7G#*PNazx5^IdDAF0Xa4hPg3NwO?tLsrQ#yRTE#ZS%M>>%ZY82q+^l$)hJQnG zm*US9-&7RYJMwWq4zHg_1RYbHq~SF9#Q0Urhj>0S;q$nsD9!%Qcxe~>Gsx$R;nIT5 z&A!s$!M;mQicg({j}o|pf0tY_6c<64Wm(L3NiDuf3XMHi*lo3I@qL1C5@XYq_F7Y8 zo*iK75Z3zN3wYj+va?f%;{TYeVHqw)2nY69qnJU!Dpoabe z=Y7k}5WngWicW^=5WdDFo(|!c&`F^~SPOs-;hA`?>_>-?J>;-;2*1G0$B)8)m|x2{ zkJRXDp`b&^y)y&p5Xw4^XBqW6BvBngTI!?@p%|?k*fQ#Ok;Lc;Ml>G{p%2JAlHNF5RL^u(IJ$U^>qlxlJPlS@gknV(YK&OSdW`d9m0R2 zp9VUFx9}RtmQj;*2yaE2L(n1o1X&JBhwxwP7HmH{gk4PO>kvA;g0Dkp zvxN^shj1&o_&S8I@Cy6UAv~5Tf8v$1GUMY+xgQ>vL#DNYW78CJ^`8tHR zF>#x9l{%!IM5;FlX)N=!W+r# z06K(Y7{9L$q0C=GmQlZoWTHbT13;idh(*J&4&ii?2hbthjaCvJLKwjh>kx`qBG4iH z91C>-9YPvx4eJo*kz1fc_!P4TI)v+3s6dBs7Gnb)!XubB&>>vFTN7AD{V@{75H^roNQZDZ%N^1o{3g?fbO0CR(H>BTu-P74_8$Db{s7f!o`qR`{MXrRaZ09J&=mf5CO5 zZI{elzkXo7LOwXg@s5;d!G7E(XQR6$E1Tboc%b2Es?*mkY;Kk?uE2V+=tb(x+5LG%~KA8>Am-ta^c}>-Uar! z@IAk5AQN`)Hyy>ds$FPow0)pm2nkbNy;a=4f@2qb?^pY2Txkf8{^p&KER(2&5S(jd zIr!oI?XN|1x=R|f5OJ-F1{g;-5kIb99xPoPX-pn?MZs(^`h|Z)Cb7WIOmKz=*Ddi9 z^xaa_gN0{3xc0~TvRpFB0U9?JYtd5-69gJ>vP@zr8jAVJk7)SShw{K{EydboAwB9$ zJ6`}gT)qbIGwpmJnFRM#pJ12)P5F{#5*?xPA&XzWWSPWWh>Q)uZ#}}o<+~zOz5~f5 zehhwhno5EP%g5jCiU4sJLWhiNvc@qD-97lBulU{R4x}5Xz6kdF6!Yyuz)pJ98Gb}> z*5gIIJV^b*JoFd#N#hJZLmLc-zZ)_MbkC5pMLra?W4JVIANHicy&d|6YsUn38&;?dE1!gbHgIjzfOx7qU(t=ofyVU-*H3A)W&)k^NuM zFXZk;*0)eGA43}Db9~PZTECE^ic0bhD#!cK^y!0@DtrT^Ib9>PoLZ@%-8W!E!b+{9OcPj#tednj&h&!+NnUhh@uQy(h(I4?QP7+i|OSe(~aNyaVn@vlevU zQ35{DS;VXKTGk;II+3`K*ShhWy3>>%3LcTzcAT%Xm}!4$$K8i4oP&!xLcj947p+)= z`jIbfJ$Xaob7i9wH%=%>q*&V$KN@*wg7G%W{EX^G?gsL@o~~t=OZv`QuRV6ckJ(-? zMB_W7akkf@UbZ!+8a-BacTc9aVMM|f_GZVor(~jB*y1_Vim$`H-O{t?EKg_CD>T1QW>-z(*6@RX839fOewP+a1nUanB6p zWHT=r9EkZ3X7v~^?E5$_s$$;;ayF)8PC!4`12OMI5*7PC3opgKJZNY? zrel7~sEMP5e>9_oY?R8(;7Of^8obz-c6TWDU5oMsrej3bFxk6KB;!iPO)*!!lGpLY zzEfBRU+i1WxRbn;IT_!yLHT0e9H#WezJFm{f3fecaTzfk^E1ZzV&4mxFE|i$6bloW zjuESr2NC;T4t^r`m6r9zz9KdE#lH9PqQ2OdWjQp{G56r6Q0yy~b$rt?J6ZT-vF`*j z4@}4GLYhMm`+k`$eX(yYdF{t^%(d(%zSuW|_r@3d-okvo>6pJTr7!jsn>xPO_gLmT z46*P1ET=E_oz3RmkJy)|$^~NIAF(XH*!MV=(>ERSJ8}t3$9#=R4=(nNp$6|QvG1o* zV-fq_&guoGV>;OqMz9;nUCmfSMsrW+oiJo(u83a)v9IW41!7-08!-_3UPI1-*!OI< zTp;$9eMW)US0sRe*!R0k9GH&bxuGGk?_Innf!O!MBm>hiF3TK_ ztZ>M5%!|BoNbLJd#)nMDtYQ4VV&4X`4T*gn&?5Gg0U!|jp3iiF>6lNGJb>6Y6BQS+ zZz@|LFdf5Hu#oAP|7M{MAodj-Um?>mQ`kg-*mn%M1!CW4NCslxZDbyZeaA3wAok@s zts$}RvCJEYee0MvFdainI3cmGn4Jm4z9Q=niG6>@%ptMwT+$)aF`LOPB=%j!YlpD0L~C+{+p!ajv!VCTl{yx&V4GdnkC=A{WWid@b;9@}e9 z7isa5W9fLcsJ#zmt!Gqp1!gS6{Za z1EzN%IG6LZ-fjav+(zXsyuK%G`RprUuaOQ{Lw%FCXw2u34fwn#CG2g|5^s|YpT;!} zWH-Pbw0BTUjtpoldbVO+Du6hvN=OglztOkc!p4e??C)6yl&%=ed$@=242`IwIODU37T znFug`#^vF?&9|a)CN5cK{sQVT4ry7B1If%E!$Rm!3=^a^-ej41WF+R#h$lZHrw_z> z{7fErt(&n(8^|=hbPP|HnP-9@Wsc;BgKODmR9tXcs2s6&aCCkit#)K)~fn?@? z1izSJ1~mNE!yjr^s~}Xq1If&fKqVe!W2hrP{%%*)VCxxFG~6!cx?K1{pK3Mfj)0tE zFf#LrNH-Xn`4R|usk}+?2iu!PC9Y_2=$;{GOWDi( zXD?hD)`vYQaBqjqoWrMaY_|d>INhWSy~IQU8E<6fGB+>icdXOeW}Mixx&>!Dsd6R7 z=b`D5ob8hbCXav(u?pAtK#u-_96gGMmU7h(?DTl;@P4<`!@-&NJ4-Q7ae`uv;&qC* zE6Rp9j2V2V4qo2wZ<~!BOx`}dbn3Lh%G+NUK;BMS`{vts^kqWUZb8-_fqdP{{%oIB z1sU>vo_!x{E9BT^&%7AvCugr_Sj2`L79&1n>>iyB`#Z3~qcZktk)hKj51Ifn_UhjB zR>;^}yYQNW+`TU0I(1#cC{L#ho#%VdZ+8Z?$j%`*hb$d(=+E{=jNH7e+?Sifbe+o0 z%ON)}cib}Mx2n4_T;D?(Ipo*dpcAnEj*@NNn2(6u+{m<}NJlw2<=3Asi&>C=^A(&< zIXUaxt#WR>#qerB7j>(Ji2b9zNQ9t;&`H>yVrS2Wp}0??4Fj1r(?+sA8Mb4io_5zT zD}Hs~pG21ZOd!iHdnP3t?cr2ybiM;w_BT|1&Hc==0roS)UQ)N&%gjB@v|ohsez1o* z1!M6r+a8vYadO5fAch|kB91-uJk;voB83xMo2N;F97aG_ATNHU-o?o z->AOqTaG?ZYVfjeTHB-S`y`YvkbTSMKVSCEnQ9>W-o)bhvhNuz!?aOSJA)i%QL?mMD{H$>&w0u zk+CoPZX#b__RX>!n(Vs{O+eZAaV(H8`+kVmNS1xS%u5Bb?_VIzA;`YJNtV9s`&shZ zkL>$&-W6Z=Ekb!;_RUp@F|XjuzDKb_havlZkX(G(_n&!%{m8!M91efW z^A$|#%f5fZ`uVc&FOqulhfI;b#EPt-=isvMaj3z2OZL43Z6mVpWxNf6?0YlY%t*H* zxh0G>WHeVKw}#BjJ(Gz8+4q04F#_55ZM{|}831r{=APvdBA7kP`_ALr6f$Y1D zi38cUoFfrzd0xtU63D)P&%}Z3TQn{L+4r4H9LT<9L*`ni6ZLfSA9Zr&Or((Pdn=iR zWZ$Ba5|Vv$J!=5jcQ&sQl6`*#$wc-o13)1Ara`-q?3a-%m1oAp8C_nFq4(3dRPq?;_r~K=%D{7A)BE{39j~ zWZzs749ULb9IIf<^AhF_WZx5*I3)YtKsqG*2H8*cy_F>i$-a+axkIw=`(y#tR0r??o)n;mE%EtQy%jHR#?k+4mMqa$p`3|2}C?X7j>* zhUEEq7ohI;CQtSq$-5e*us3}#4CEl5*2qJ_cV*kMML{PrMN6A=VkW! zBXQy9y$g?|D8+tWN_Vzby65Eri>%n`NNnWgqbNKNHfn#Hlyw3Be;_?C!7(4rE9*<} zTUL@JJ;&}+G}2oZonLF)P0NeUnpUzL#x;%DeEIUC5(vdbXdXSkaAJvjaj|<;#r%?z zc^e=oFPS^ft*Q`F`Dq}K8m3b0eeG@*upg^qAA!7lUt#&2tT;y}I;M#O{e9)|kHrt& z9F7*C=H|i;Aq-bhL2n3Z5Xb-lQK=%JGN}=n954e( zASIJY5U^GeaHvzU+7|n&hSt`hwx!lutF=W-ovJOiwY9dd4?(JScvYXTwtsE?zi;ie z&pEkdxJ017_t?p~Yp*@5J@2#5UVE=?mLerUS5kHRxxfG)K4EzMvWmKuO%T(6h;Fmd z#a*uL+KWF#9?H|^p~c=0C<_nib+7opCKulayE`!WJ85+7`i7i?()H$5OVOXvk9mts z8@8U{awzM71LqKB?0MbIhyFmiPI+ka(`U^+%{izlw~wlF_mHuZB?`YVz~P8Vm$8q6 zLm_;I;=wD+m;Lgagg96xG4Bxx8H=XN*t-kIccG0#G7NWr$iZ5trG?|E0KP4)vEkBX z>~m2L?t5i9dXlkk!Z+iimI%UHYr2g6^C1~FW!bDh!s<{QSe^NhVb7;UjjiW&8T(zJ zmqHlkuRk73FI~od59qN@t+DxQhC`?toNb}}^(15e8R(@fF+fXieHy*zKo9HYlE0p0 z?EeLNzqEx^dZf8o?Eqq)ML{!>v3nWH*i#wG*ylpVz7`px$O8-ZQqL?G>(2INSe@q} zV_!^*8e1UsGG=F7+!4#wk?CY(-zCt!rRjj0g72l^x@9kS9#F^cIbcUlt ze4Y>+30SA>ds_iVps%tHJshy)+|jGPUyK3S7YjI=;Q`AOav7dD=P^RgD}+lF%3>ez z4T@i{P?lTZevjhcR``U%pD27;;ckV0QW$6c5&sZ{qZFR6uv%e@!pjwMpA+MW!aJs~ z_(KY%Uf}*y#dj-|dwAf^d7Sa(9v(o^ZwM&&@Bqp^Jb-c!51=d+0?Iu+fM4`Xe7T1Q zxZJ}7DEIIH$~`=QIT*Z5=WvB`4-as;hX+vZ;Q_2vf4PSTI3)v&_n<;K9|Qciic>c^ z{Y8H(pvdx#dtQ10P@yWGPADEIIH$~`=Q)3Qu_xrYaM zrQ&iA4{*7M2T<h-cc)Lg5S_^V1lrdMm$_i6!et7cAzY?B3c}@bOodjs{B6Vv z376SJRJeQ_e7X}Zn^;kvG7brs-(=(w`OE_JY0o1za=u_VSbZN7RpIjaq-=%D?{Qu> z!sVM_RpIitV5M+*0)4yIr~MeceBtt$oSuE*@(Cfo%!SJoNmIDI0{IJs%d1gofj;ea z`jwe7#*F~k7cLKG9wr&Tyu``OgD+gZlT>`+^4s(~!Gs*1c!PeOh0D(&84)ha(z1WB zTEy3ZaQRSXCOB9vqV8S}R+B9d;j+}MFI*nYtoy=c;Sqe{GV`)`!sYYP=oBvhgE4*K z@+PK{E?iDANFZG15yQO@E?-ZQzHnJslWv5|C$b;NgnD7cNJc zf-hW-u-*C~T>ccP_`>Cr*xua;myck`XH3tSn^?)b_`>Bu%%?A07E$vHk23^?%T=`W zE?n-95`4IX%RB%p!sSn~c!6+Pln7YyaWpCXq($S&F>DEo<|Ie4+5+KnA!|4gF1Isq zAY86x;BJM>9Sj@@m-{ntAY5L-Y7T_UGwB-$mp3xqK)5`CXdqnv6AKs!mtUf9AY2xP zJtSNvQxHB_ErQmNaCsa3dlxRtcR@(F{8tn}gv&Ak1j1zzI0gr+uVm(WI9UCA)RG97 zUuAU#!e!a-5(t-HWp(u+Tz;8_2nm;uB(*@ed<1h52$z3EG!QONC-FeIJdkk%;j-+P z353f78Mj;E@(&p|5H8D>lJ14eA7{cn2$v~;3=5a5n083G{7=RY370QpFA52lf5!Md z370!iZF?hJ9?A>^!sTm-_C>fXht-#RKLhi6*i2?}e;uqA9ofG|HM!TgJmgG6PTJ_x zUT>teMxXY2)u;Ur6v|zn&p^IDZEBJU#I&WvoJ0BhzgA2ed`IlPs1;lx`W1Myt3wL4 z!67?TvgA7MWw|*62p2T6THeOo(hO>n13byZE( znzWlA;aSzX4E~{2xc?>c(TaU^Wca|I74GVgu+hHG#=|lC)`r?9D^Xm!vbD9gsl8$; zI>WN&R`}PqRV-ay)y37B5T)bd%%+tsq#+W@3UrNW^~)`p|+JonV?M&Vcgy;#9 z0JHYRAY5$-zX0rOf$tG4tNCU=*6X=WHWUxzGaQ+MqZJ1yBBn*51q@4*F1ze5TnWP3 zIE=?|9}hWL>$J3Rnd;Q8K{=RtmV@hmwj5k4v9w`zs8f4l$iZ6EWtY@}&3Yg`LQAh2 zb{hxQjCE=^fL1_z*uP52%Q=rEUo7RvXpUn!bd*h(YWS3t;xFc<(5Dhpxw(rsV2XJ_V7B#jUgog1k z9&bV(RRTxKE?M8qDj~}nrk6s0nE*FyEIo@`7>=DDpx;krx7rybw_2g@7V2 z1QdB8pvVgWMP3Lf@&hi|0E;$io6g|eUuGJ0@|y8#6xVi_bu-Si4tw-U+4SGnMCU;)?^XjWPj7p2u^((P@EB zZD%Xa)aKnP`n0KEJFf#ewYNg2HqQLI=4ZA zqC>k0{?JYR(yQL$NNOipO#3>!ngS`XcckIe>Zsgh3Kjb*KzaHqwHhy&H znPX>d2k0OUbW+be^-k!`zO_re*`twWy58&r^j{Z<-t6;yz1i3jbRF{YdKUH+JSsr5^41 zyYRk8UZ67@XN##v+s&Je^Tjvp?uRqdanwgxclJ;{W9)21-L1ugGsb^pIwda$XN=2V z&dNg@cpcYypT`;F&qzMN^BmNN{jWMRoPJ2yxfjfx;Q^zeT&m%T+ zK7Qp)@Q;zGif!{q*@|sb=))?ueE>mJZ2K!%DYl&h*p1ls6?&`kiEXP;brjnsnIT_nD+goK#kRj-Qi0f(=Z5z}Z2NWAhcC8$oV2Rn z7u()KED+l+XVBiowj)vU50}{XHk4Szwr8>#0~6)j+ZZ?y+lmA)5Zktrav-*CU?u~x?L4+gAhvyjfdjE^ z7AXf}+nX3T5ZiKOhV@}TOuUCP!OyTldKcT~kxWQz`!(cA#I`a41Y+B?Gr`jttp~9! zj{=L>Rz7C}vF&Y)7l>^?#~SECZ2L9~5faD;?9?R+t z#I~#0G=bRmhYZ}U*!B&^4aBzd7`J<|?FQ27L2P>+sqMv?;0`7i65GDZ_&tejFF>{J zjo9{CW;hVr&SE|GMQqElYR?2OVwlfK)6oMV6KJ1Sn z7w!~_aj6gcKg3UVTtXlAH&IM?N;T~uB#z9o7dR9-{GIYYuN zFeigg+e07r>GbyXVG9O**jx*64N`8^VIPlF?eSjcyFffgwo?DEl@#|)$l3Woulc^+ zdr5Ow`mnb;Zry~_=0G8KBCHU5*`wp;ESptaY_(y>U!^kaM^zd26GjjArFY&1Ag7n3EPu-tkSaBJ8lyE)f8mFv9lhWRQw^Q&0CHit{4sN&w07@HKW>5CY9|LU*) zP#)RcL-@S!4(k42JDl6;SZ&|NbyyF(9o0P^&<&4@11sJ>s0zESxng-$Q%wU-2)9hc z#tUpS!9jPP3a)6XT@6`0T9ZfLt!y8gIjR^~6ff{pu^cM4I{@k$soiPCqJ$<6F@h>vu97Le9=c zl!IU3EJsgr?kgcr*lLL&thJ`gxgW+$*~*a#ZM{^7;=t<6fFN=*Eoy8%Uj#gyzpbE0 zxghh0g;<{+TiE6SR_8L%8%c{A>z6L)-X6*y`3{@EbUF7OpqH}504=@su!r;aQYe2t z$+^D=de7QIDm~KN9HI@Yb1e!Qt{0o`eegq*wlECMR)j;+zOmu3p3QK+`7Od_BIk}{ zK|T{X_hj^!kNDJsdI>p+)#iuDfsk{v?gQgGtOu@w8WYY9>xVrlF#AHz&HiZ(`zz-G z8qOt@dzTtie(CSaxqDZHZP?kcauh_r6%Tb_->;BmJ*W%&pzK=~i!fIolzn47pdJqD z!Z!6_bzyhl<;8kw#+1loE1b9XLPqW@!|qK+KDo54EG4G0{}rdrJ>8WreIy3sV#iK+ zMt-gv8F~AaMi+LZvV(HlUC?j+O0!b-tY2jmnux<3J8W!xU@D&u~f)~x6y0FZHCfi7lc+%F+kNXC6S$f}I{5%_c`<2JFP zpJS52ozLH3WM9TzLu}-H{ECd5TfS7r-9*Y(#$5u|T4mha9i}pF${Z-;z5uWr8TVWC z@@3ptl8i6ot_t~OF5~9WUdp&-qku2tK7my_A>WiEu@QL>jwXwOQ(wk?I`iPmxaEAB zFXL_?6<@}E3`6=d?uY2tS;oy%j3VRSKtEr`-NJZ*jC%|-6Uew9VX1pLn#|)_BIA~t z^<~^kNO)1c7IGOA^<~_0qVoJ5aElaNi+!lk6PP((l)MQ~zx9Z8lrzX37^TyIr|{DN zC(5{wW`=wj_j61mUB+F8;2|0JPZ4G>WZd5;Nngf&6lrxMhh8Mkaf4P@MMDm9RC%c0Uh#=V4X63DpUA>}~EEn7YU z8TXeMIFND6!O76kiNT;W$0`2Nn#-d+&7xz%tpX{)34|*TCYDTLFONM3SBme`y!+D`eyogH%E=y zYr!DaZefZBImfEK{uy(7EmMD1a?6~OEi-DbpEM~OwbxHd_7!hY3u$7DF zbCEbWPc2(roC>-fl#=(L_If4H0;85bu-@zIq4#>iXb8z60uS98P0iPH_SAe`GOl=R z=^SdmUQ#D|ug7Mp_nMug8~M1e_nKiiulwh}VIR$2iN|sNjBXc}o#S-Pr{JWAZ6run zVs!RQYpj5g2Xj7jH$wkklcGc$8)>O4u@pw8fSced^ zuBEvRhmy}YYe8yyYC=(2QCX_6wHBwA+iH70om)`{A?XU9XNK&wc_koEySLRY_4}+i zEXUoyffnnf%d5~iTWhOooP#$^A3Q;RP^^xjR#a14y>eMa6?P*m!-!NA6Tefmw7KyE ziPgK*eI0&ffCUtjE>|B0heG&pU7BwghNB!gU9SFm==B3{!{z~|%hkIJ$M<2F1jF4Q zaTZ$d)XFJ>5=AUwF8KG6ofL7tN$0mWg=IHM#_xj z>Qf+BUyBSOc#pcTzYDqg1)5g^TQ4Cev4-6bgj}5;rGas!Em$67T!;STFD=ft;@7*y zt&MPh$lWRDw}$-%-NrY>InHUAN6O((GN}C0{|xnGIQpM0KIVu1mZtzeGph4#)kfEK zSo3voclAn~CGB*biRGp{om;;aC)J(zsgcer3L7{+5RUKGKI*?-+0yE71?cr&?D5A>nw;rgfU+!P3hYRk z0CQg6$=kjgS{HUjcZ}POv+^uP|H2(SgMP>E_w(GHk<<&XrSeYQ={bXOj{do%oA;iC z|I7RDA0FLt`>RL3U;GNm^?1g5ltYElC*yq<9q1$yR4$!mluO5opiTOeoQOzcseO8o zJJ_8HrYRfxpF`@P<2XnhLi;6<-e6R9|G*tOHugAe{8Qu%jR6xyVC?YZ4@@B*_Xhk} zNi1J&(X0sHc=4>r6~wYFI3JiBkNn;^;ulh!XtV+_f~tU5G)i$=mbgfY*^%XNh-b&1 zraxIg+z@yk01fltF3GNP+@tB_UKnq!ZFk&b1u5@03U16^|B8y$jypCVR+LNxQpeW8 zioG5`1n`_M@poS>IJVX|Epz&|HW5Aw!>%i9eD-9ERKBWH9P38b%q}jQiDkOtk9K!H zh2zE=*3{hGC{G$XVD+>me*qgC2(Nu42Mb>Cc(j=X=MN)WV>aefJ)1^qXl`1Tk{>$y zmc3su@sAwH8c0CacPIT$NZ%?M`9=#Bo9v^CyEtkPYHlAcwK8%V<(4}mDNpf0?B~Le)-}yyf6D9zc*<( zhlabD{Y=8AX{T?u*D;~S;l_T=`1V`yXvOi#E9NqV7BDPL`ZsrX;mBj#IE=?|pP@yK z^_vFV+Udnflq1R->zDq`%{!^KAP~#Z(>FK2^`EiC04=@rZ|)Avind+|ZM{^7;*iXP z7}tfgsIm2&{>^<4=<%C^`QwCZ>7{>j{|5Bfo2{|=YlZ`g=$nT_`RnPM`){C^@?!<% z-Q$}(l)s+7xer5!z>x9{t$a2sv>y2=U?$((=OJ7s-`qTJoXKAHyUbllOpBe6LU+nlvWf|WElgdIS(0$7U6rw+) zHB!@dx=!mZ^1`n%B$>>_% zms}-y-^BpneV-(jZNX+>;C=sK9Pu0QzP}T%Fz@>_dS^$z4glVldGKDGgx_rUANX}o zbjh~SFe~nocw4FCPEsDYZ9MGEiF;1G304Mm=k;sF%iTRmkaF{)@*L@22ZHgfZ-RE; zhKAzm`ljNdBHrX&ylm;x;;PohaZSxlwc~25s_TnIy4LA}XX&%T1w)2bQ(ITG+_ZzT z!H+v`WcBQlvN30lIdg2ugqJaL(4+W{37^v@ZVjsoOPJc4tmW(QU}j)SuyxG<+xDh^ zhyljIMu_)5C>N?_3>e-6&PQj?0s9qoz`5W51)MMWS-urx>_#wUc)~r4?|+(nJPc=f zfNSX(GW4T477tj6Uc-$=7#jyxhx9I`MUD02yUW_?#VyqwA=X&G6X8G}#hSH1I)Dtp z!&d>z!F-WVwQ*o|UIg+oEo!XaY~W#fDNJ{aPkMN7`1Hu9+BmQ}ThjPNTYu-nFHElx z^vDm89^GyJWcCJb&34f1ioaWd0AYHQL;1rxj8AVp?BV?VXIlQOAE)3jJ+9H%{NdH^ z)BBXtD*>d*MTf|;M1a%E=XQkvVuqlAUGcMDhA&98XfS6E!gW<%zR+ynW7v9xd&out zv{@iDZ$;y|oQ(#@p#TH%u%1~imNiUoIUEjwn>Ci6#VriG_s-9TbHen(DH3OH0uzy* z8pg9lMijV&Q)m2eixFqb2PPu7t%h;dKwu(L6JdOi$f)STIe`4DeGM5Ijz86A#+%|L zC%h@(Xwc(&DfQ>VmkNbl&sz1=AFAcC$;^xoRXV%xT)#8bMk)tnz!|aol%6xy6hu_IjNga znK-9aGA%1_>SM2W%Zuv5Yy@kxab2Sg^PX8;utjy)bzAVF;RRc}T$|2|f;9~tu;D7L z&&_g`R+nG~3$_5YO^v5oCLRqGML{DS*h}kKk8>WktOvdS%C=SJL&j_`y2w zTG}73#BVm&P241Zj>3=6(pH0|Jwp7-O2 z=6?)$P7Ah%xFaRRg#*|dITCVOuu6O-EbhFViyikEv7UqGhz>*S+*p)}52UNS^bR0z z+}65Ow|b5%Eh;T*X|3&Q=Vx)-nl>Ff!Vt^$G{X;9gzR`lNc%=cx~CVNRXVF=&gjPC zWnL7FF@|y{zL?|H8qPs;bK^I;8*rTw?52+#jVieX7)Pvx>}qwGQLU+KZEpO43^6B% zrJ?&^I<3I^NH;6AZB#bWr`>u!W%lt3OQ$naE{O*GzrWT^Whu2asgQRkYEpZ+A7xql zatQJd^s@2)e8&01#5X@|dpaNnhZw$Bt(^{2fpFwlW3B0Y^C-+-oI}Z%_QW^WVle*F z5(BjKX2Xuxt#94|y*Mz^>xplk0@kR|kAUI^wz^}YWBt^n4k z@fg0@yfd)MD^soTov3@h?>r0y_7U=(tY1RmPvL3#<&ks;K3ZXkLd!RwrubZi=PKlQ zVZ17Z4GLE(T&M683U5=`L5O_ZrSJ#pzFpzl>P}9K;kGK2{G+c({^2g=#E*rxoM04| zVy&&s^4Ptt^_EVas2d-8=dZW0axiqu1Qdc-80w_*u*V@~)_K>K-u#;9ocoGd+g-bJ zh*Q4nZ|}eVGI?g^k39I|5{AEJH_!b$;Kxyv{*y~S1^is-_@~He{;tU-cV?kn{h?=I zR|)JG!u>tiQ_c*sgomd{!?|U*<;Ojz;}z;a__OM%w{r{VId7gZ?v1-z?%bWX{mh+_ zwa|&sfv4rh-M4PMb9a30jl12c9lN3(H|`!#3B3)VGXOdj7M2bEvjd$CQRs7ESf~6G z61E0(sN11aIFzBh9Qm1g4W6rZ&valf*|F=o(zUO-9oL}_&fkSK?3bOq&%*sS*43_@ z)LPir?vCfJ-5rHJZ~Kiq-#o*7Td-=w`DeXtnSr|)kbmS|$~gpicBt#29C=5cvuvJ` zm;S(+mndj%nW5h+P7Bx72T{*|3)+Bn;h}se`zyDh-b!(|LJYK62U&<8t^?xCUyFYG zyovbCi+oEVJnQLN(6}t5gCd$TbmU$J^Z|_7N6@CbhdPUPT{CF?j%dlv$!Mx_2Oh^O zSr4^9tm`Y2%=4R?4)t1C{CmhZ>*38)$Gs5`r@gl0m3pMhJX{FK{=$06*ZzWahW-QK z$2$-z;NKthb}Gu6US~tg7wvS*JMgMQe1QvgRhHbDyry)0UbJ#OM0E3a#heawy&Xg1 z%;TxrR^>PD?muC@7RHS4@LPAFY+dIR^*@#KojM@Ww!_aUuwCyXNYym z`eK>6AL!;T+NuctY^$sf&{iWrV-OyzFC^SnF8WE-DIbLP!kFxPd(E@$g}%b}7>cs7 zJyFN6y#3Gj{~&cz$vI<3V_e1!oavgxNS%_+Aa=vjziWDXzYqR z7=u%9+|9n}_tmMSHy?h^BZYY!gB`oB&c70UZQbsxPrdTBl(S*y&`|$kdliBn<|pY} zxeod@+X44uFr897Y=<1g;rvBCBRMF?^T`g8X=kJ9ZsYe$~!loSP6YJicvSyyXZ}dG*d=NUy83 zY&x#f!Lk1e(<+yGxmo7gnV45u4&48N`4#2ixGGt{V*uuFb^kQx-Rop*YM&$>)=duT zmbzXzhhd%M_b7@O&hJQ1=9MWagvsj=-8Ak=Sc5qZIPx;KWW(-gYJ)#_moUy$lfDg4 z$T98WeifEqXkkvp>--Vv1E6>1*nk$xPj|MtDa(yJOG5Jq^UeN8Xzb;?IFC7%NCW*7 zbo&n~N1CW_(u#)KEjsmT%>Q_V_9D_DbPGn%-LtEfIXXgT2?~9QK);Y@pnd$Gt{U_`fj@PB!w70b? zUAW5P2E*8)Sg1-QC_}}U#emQPR2=$}hA>FKd}r2>V$K>vLSYj@ST369m<6x_b^$Dx zVY!{rP#%cTx|+q%dFq>Gd~uTDSiH>#ClQCc8}VI|g*3s|nWe3yriC0c_8}qKS4scz4ml_NTfjiEB!7Jw^oM|HivmkL(!7%G1R zNSDfHO{j*>6L;M5&6=owJ!jdmI-mA7Teo zkYC3c8{`9LXM$i%zNknWnKex^#huF%Y%VPc%Ir9$p-@@jEvVy_JQ|1Ov9nt$O3^Z< zq3A@-n4*&cU#w-5`JLQ38M|}h)ZAIv2|ESd<-{UK<^7%Vb5hvZ#-9?T3+0gk zVkkLFzyx4O!kDGg4t`_9oQ}sm9X=i}ZotwhCM+(UW}$Oo>9h)LY_N1X5EzzDx#WnY zQ+@%&v!h=@gm|{BnR0_>G%M@th}*B)odN&YXYdov9+|xbb8&WR1+eUYAhqm#dN}bf zircCSkb)=_2FGJi9s%5=zEK49nXsS znnaX)^R`HU$#ngOSRX-ty(ecd>|p$OKN8n9aE%RN(z(AtO5Z^EVX@H(UM&|pjxJt- zGq7TX6B}cS7EOE(xv*F~F&#OuSWe>WjHV&;24XXCZ0v7@#!!@D6>RZ8;)naqr52GW z6|npP&!|{w;xEWWY`m?)7->zgsV+vGSee1%i8ZK#*hGWnBxW%WlMI%Z7)0!NgAGkQ zPAZcPHY_oac|XBm!xKl7%83R`IeA-=jb+TE8=IaO#dtI5gbKj81a=y+2sCvh7Sr<# z76Yi4_!{#z+n`w1jZ?n)I$}r}k8<)3MJw}ui;Zqm6 z4(!Cy9Za~n2G-oKB5TuuUKsoGz={?pcDF_xh%-yEdo$Iw1-LoU*z?J^V2K^?kt(`Ik|(HQQ@npl z(W1#Hi)PVy@?P{eAI(WNGw#V6H!nF7G-9WC{jgBt6%0-CGSAql>RXar!@$!#7S}5% zORfNn%~0RT$&K_q-8)+1PDxUeQf!uIrkknBZOqkd4Ll>ckj*wneP<>=LsE0SF%oxf z@@U3ATLYIT|H8QC9-9G;mHZn6w|cL|i7rlZ6vkG19DR|3r@iEQM!v-3z={++=Ox>S zukk(t+L40aWF>inbnJ5PG1O0_U`JN6n)nsoEb;$i7Mg&yomI->L|oqTcF125_T#YR z#IoI_Orvp^Zme!z02J#dO^DY*5?q24>+hP{kAoP~84&#jeh)bXg+3fLoEsbIChujy z0+((i^9Pa{rD!bKj_|RgT~6m1jZXY*1S6sy;?QOketT@Wmpp={U+B${fKKu>##^LS z6;0kus*62__6p+364v^89_MSXU{sQKxW>-cYMzUrno#NkhleZ9GrZ~DXFKsb!}y`jp-%ySGj_3;oWlBS_K34te#(Sfyk82s+KJP`X-VjgaN_KQtb4_W zapb&-&UqL|C)$VYcGw73?8P3_bmE60grAdP&fy#{?hp=WbQ5|Gh;qyv&i?A!S!KGo zm*O{m9)8%Hv?t_JjQc(!Pl+6!_z~PAM;s^3>m>e(qC|#MGV2w1iSdAu5eo&2CSIc7 zkyV1l6Ejev$Wc=T>z`nkjHKoWmXqKCx=6mEnVWzt)QOCoD1Lc~*HOsG==YF1W{bpQ z^gFIbuwjW>#v5b&h9|gA85wK*Qi*dJZ=CTPmAD<$B1OipFp;8PvBAbBo@TrfgOw!y z90gX&5@Y^x;x(|)OR56LmTv_9NkoW8^AZygG&;ycbP{)?Vxof$7IktTLcq^Tz+~cC zls$TgPM(RMGT@;Gi#kXA9Rb@L+nl_tyu=Nlm^J7vDV~$K1JSYu8!Vb=LSeIp7%ZN+ z2}H7n8Z5`@&rg83Tb?)tQQb2&rGe0l;<#tol%jt|CAj5GZ}<{~zl!K0nN<+W8Ox|k zM$|2biYi8dcBy*DT`bmeXZQxh7)R$zBnCrw5H&GYO0fCPQA5BrxQmf-_bQkEHB4j) zDP1i=&QHCGN^w6?h;ZJ`PX5nH7WS#aPtu$j(N@DqdmEhc_?HaM_1wpJDVMMsgx&j+$Yc}+-e3G zQ`Nc**4JSj`)lU5hOW+V-Z9^S7IMEUQCPIQq7rSkR;sU?|~5IlI{=0f$8x$x4Xoo`Glld?i61Q0h>FCF@G!p z8R$(D@XO5dk4(T3vi0IQzUJKdBOvp3+~>t-!I5nEpO{dljb0#4w2|chX~fup$K-#3 zw$HKK)!8-;s{AvEMKW6D#cwfZWZB$TC1=c418a53ILX;7CVi8$oszSKCTF|EXMQTi z(7Od$bR-T6{!XkY^fsI>aksLw{Y6NVEaw?Z`oARLVng~JK^BgvsmJD%{}yC^iR`(4 zmxz@Auj0JGr1&>M7MaTUJJVu^T@T3qcR$kFf=3%vdZ1IV3BEkWvki}JQK>(C`8mVz zgo7U&S4SHXwfA!nV7fZrlpXq{Ala%IQZPg{`?w)D#!@LY9uEmG^{Ys)|87OjJgj!nsRp?oN}f@`>{}8*H{wO=W#SJ*7|PAr`+4I^}oa zvHUuqbEvtlVxs(*a<`a7nc#dpHqoyuU1##I8I%*V`;hB02tQTbB@-~yd`ko6KZ7u= zFnTY>ql3asDhr%rE8*6HC#bNGVCliV5KitklL1ogATE^6z1i2MY zS`jy>bEt@QOrf0l-fmJL^?UG`6n|Nx?VDVJ)M7Hg3ddk zwyDN(UvmF}dLuXHx)a$G++Rs&#)>}7#QyYn*>vx6r}Xb6{utf0l`1#=H$g-85fh9+`m}x{j3CEZnCidk>&GqEN3iZU;o@ZiD6v9#Al<{^w z(cQ+NLk$JSzDFpW=+0qn9B$m{wnf}ea{q$K&P{nPaemXgOG@8lPHv<97!#iEWb;v* z?cC_+Nr;6L+AkICFATrPylD_}2cAw%Hvib|>KruLfGC->0a3DKr4>U#;4cW{&hXr~ zPyi-2BS>(%Cm|M0A@k;*<}rvQmxC~)@O0{f3y&SD&VCm}LS*SmKgom&S;rTck};9F zcu=xBw72=zanY6!GvskhuG)lTsLM=9@MTrn7Eb9~47i;nSD1hd#MMjmsIv$@szJvO zX8{#E$N9+5(IBy-B4q4cH}w$5=R|pdE=j$}K6D!2Bv}zqc2d8>k9)dt;73C0HO4x_ zIAAQ|`w#1<nl|;s^8yh*dC{k2ZG7fNU?b*eVFJDm^WtTo31FD8~K~F z?%LVQ=8P*|e%83+FB~^MvZih#bQVMg%$~h`4m6G;Y-8lb%V$Tv;ub}Id3jOfF?Zda z^~)mFb&;1ZFPghHa=SYld5w&DWKQucCHy6LM0UEC@JGiLZyr+`xwH<&AmIj-rmSRa z={V>zEh)xdN!jS}#gUyKFN)mZA$PlAxyM_F`WRna`-wTrK00SvNpa+RAFrDmdCFV& z1*Y@B$BU4}%ig*Pkvl(Lv^?^fx2~~h{DfsfIkLJ4lp}*@kBzhxMTQhbRXRg3Gw3*{dp(b@)$(-VAsVuT=EC@b&8Ge2eSr_^GWhiWCx8s?i`*=qg zXQu;jo4BWLalXR2G*aP_+{d6=w0JoE%Z`wqai!}5+D+j_}3!6 ztD{$CZNSW7%S3jRBA*$#Q7M+CP5yGKxObg4`MtH`-#Kj?pX}QG#{VYQW^86|q4g)j zloJ0O6z`_!dUu_7rRD|B!c5J#{-`UK-R7ChOE}NA{>YA`5Qly=E3Nu!LrwDalcJLn!T zRd_*gaiG4bzP-Mxp}wuQqOlqF=2pG2vd!T~Y(?9udQqFo??aP0jFq1a^k{Cumn&KP3bB!$rt{K<=C)dg9CC%Rl5ef3U0vVq zkS(oPQQy$ukd>r%;g}3uB)k9n;Qh)5x zrY6~@#_;8DO+`)U?oYX^Q?fz5wY>hbY%)sNSlha+wrS}a=$me=YOyzku4=7narD;E zy;t{Q^z7D2KGW|HUA44L?h|d3daCX2Gyp^;b>?uSQ+k+`OV*u0>teu(H;vt8c1lYiJJX&oMo(t-YeHrK+`d0y>YmQ?x8+HmfoSq|3h8k(0O1DCYR?X1mp zb#2g@B#BovR{wmu zuy0>d)gbUvoD65d12xLEt_BIAajil+MW@3Z6|EQ{NMa?1 zdqq=yb9I{oD{7{P`HFe2s9V_t-P6+gAq~_`$bR^)X{_9$_E9Gs(`;iD0$E0iRj7Ba z$pkZ8`=u3)^-a81c2zAtz$#c9@W$bks)h<_>_!r(GCirK3dd27V^3!V$sN6B5|oa+ zrj~lBS2nF|tF4iFz)m&N7o7G>M3J@J4J=pu^0r{Aw?@aa3bpNQ8_b*-2sKbgiOJik z!BA^={K3W&0%vJgn$p&R-^FC#?x4w=TQNYWntSfq)8|yoJ8k-cdFRercv{7LzimsU z8f3Dzb011&=2O49%=sK0QDtbY+EpY_-_#sB)Ne}9sTR3qr6ELh!wOSD(!3QFr_Y*u zn!~|@>73cNmmAv|98s22XD4 z)#_C>=n#6zGG1df&MI_$NpflPN}g1zFr6IDfp^@}s+yWs`i7OOs{(Ums>);vHHsoj zZ3IP=#=~6U*A|BuYDl^Xw4uwig;!C9iOoip*R6j&G$xJ}6%AErvBoucDPY{H2K6qq z3sfMV$ohi0&>bcdCNx^rlCt!GrP|>;z4*}M&=*m4QX|qGI0;Ktpx>=xi=l|KUiSS^=~7&8KpSENzj2C_1&sv4xjYWKiQ$fR4D zc5_3GsWEelw&gM4-6)mM9**0_Wk5@nGvDYE9LMAnLRW61U!rse{=nG2&@<+W%vzqL6$e-IVLQ1`DU#MM~sdp#%DP4vq$40j!yzH zKM!$Kbf6#27(5Ig4!4CKYHYeCz-=6Qk=_|LQpj&w$j)#_fHNkCY>lPCGija5aT54- zgk@0Hfi1^8*liqGoy$_#=FvsEXTdK_Z`0|fT%^bHS$Y@2PI}|<&}=L)eg-pk`d^GE zobIg%_l{vxC8({JMquG|JCH7gLQEIM@zY%gdzjv}$2bnR8(CwmT&4`uYnd681D^{% zz0bgII->|e{ zb#5;1`b^q);D=^vVHlcc5bj80m0wE(pCx|ZqsMCq_l$%vKQ)0t-yoz4tR|;S`vj?G1+Bp1K>@qXQE`kF?*PW8n;? zK++yT2v@#c3w;My&M&fj#zbX&GcG1zKR$6|O#F=&1%3ASAeT{RDsYYqW1^z3+vX`8 zlK#{;&i>p%M2w+NX@SK#uI&1ZEe~<)UjvI>pP@g$&6oQPYRb>`7GsS6RKJ>yKi68q z{=DqW;>;h{Oe{|N+d??~xn^RFp+7k*!0FF*5o3(M#aJKp0Rmf8@*nk^)#Q(B9mbgW zesZCjr+-9d1`R4WM|NN$a>_D{bCd)of>VWI6Tt~s_5*9!PcE|vAn|MQbl};Bhi4MM z2xtxczk+8A+G{Ex{T~Fh>tnorxN%xe$<+!hR|o4S==&< zB4$y`E@&Ahl^>0|nAKR@$mIj`Rj6xOx{jr5Se@6el<(GcE4yZ8zuC-=QEt%Ucp8ou z&qhG6NcADYeG_rO&nx5{>0!9eCxkoOo$x}1l?qz`hnL{N95}qx%#94kIgoHJ;P43; zBZL#dE5VKK4S0r6Hgg^EpA*NyV9ssCX8;ZdkN74b=yT4Zdz^5pwGPs92tA>NfO zieF2Jc+0^9Fy01*aK@nlvo`C&Esr?zJX9g)UGF%@8KH2b!qEyrO8n*QGQypv_*{kO zDm-6dt-=O{YZYFv@NR_3MVN%RpDHP zixhHaE8{gPyiDN+g?B3as=`MVzNPSQ3bRp%q?1%wps-ZoDGJY2c!9!tg_kM3O5rUE zzo3u@6iEL?g}+t!M}_}SA-Ci(+=&XODJ)l5tB_k=819P-zoGDXg}+wFtD_n2U4>cb zd&H>&jF39M2&uc4aHYca3U5|;r$TOPWH|1HAY7sFQia^bM)yq$?^eiTCUpOy!k;U| zVLEYtTcL{%&Tx4Ok5X8q@FNQ6D!f493WXn2c)h}ZQ}}?wClqoUDbsmb;TsD3VNB6| zu)=(W+*VBY^AvJi5?`h8YK5Oxc$dO&DttoW3kr8A{IkOM6%ND;jdYJvSgi0Ag=Z_Q zP;%%q@SyBxWeNU z@~9pCPghv4@M48mDEySd&ncviX~z43!WR_&hr&N8j3!LDe1# zn7VIN{8ojxE8MK`A%%}9e3}sH{7T`g>i)XIKdU=+12fzJLZmxL;V^Zdqi~73S1D{) zxLV=o2|?#>#lNEP0d@bC!gm#VkghSkn8E>sh(AZ+VuelW->UFhg*Pa?UEyYh4=8*{ z;md@e`#Z&dukbB(&&NkM{l^g^KcxyMt9!G;b?Sb#!rK(yuJ9X#p!1mGybp}@o>up_ z6%NJ=n&FNlg#So|qt*R*g{LXJNa2-)2!EBr+Z5iea0?;mepm6W3ZGK<7Zv_N;k)Yp zcZJkx&G>l=k5X7lh;%0^oT+fG!b(EKYf!vJ;VN~%Ug0h3{#k_&D11oavkHH#@D)PP z{gdKvDI5ykjrkd-aE!uYh0_TU?`*}-RoI~Js};XY;d*u7r0|Ohw-DkT_btVrQTQSu z(*Kph9qOJ9IV;m0LJ0pu6&|VXCn)3rWQJRy{^u#IQujv1FIKol-LF*qDup+z`|XNv zR``^{A1VB;LMRoHe0g9DnSL%I@;O-HG3q`^@e>uEsqh?yD+m#PmEs>$_-S?DtoWA{ zeox(>Rs6>aUsU&93g1@9g$>dlpztt4w8N2#AE$V+;wLFyuK0zDpG`gv{0!Cn zl_*}OxO|_$U%pQep5Gx1->&`}6~9&S`xW1!_%_A4Kc4CRT=AC_e^c>y6vtwf(2o-0 z8KyYDdx)1PUZ(h5#mg1HQ1ME|xhBE~pC*=V!54u+ zyZk%Gu^hn5iu~C)zDO)P@(lo-HkUNqxSJ1@hIw$8WN~w8jBaju%zd3ON3d*ISK{%U zO8)L6W$`NTH|s3Y(6SxU?pr+UtP(HA{cotGZgo~N<91&d?WPXD09z9tvxospmsc7| z#nThUgzWPU2HCkFTNtz)X&NGZHIcq$?>Ec%QdyDO3L*LN-6^f@gmfi!H3bbwbc*ml zQ_$$&4*V~ZEOvdLQMiAZ>Bi_t#AlUnUX7de&7pANpkz2W?IBy73%9UgSeh|-7#^Sg zV!8`g0$&@40U54_7B$vy8gOf;*A_e+P1abyli?69#~+c24!E%#oUUv+=D}{`!0OBc zvW6Bl*6%FfVR|09K!VO3gwUUlcjeJ>^2T*h+mD9t z?wBP4oK`-Y6#|Hvgbp#;B7io^eXzr;#da$F2YAYl)%w#I(g?RqghWxW`1G{XUw zD6~2*Ii~4;n!>pX&s8Yv#qh6Ee7V9Fg&!kCXP}N&!duk6QE`stjC8}VX{%jbyYv#v z`1Pi{a{R;-CZ)vmuDkN{W`fwKe4kZil@(G+ln84+igi}3l--#ljHh#i{p7{*3~LJy z`n_~R`!-n%7ne9nKtfW+F5Aa*Mpn{D7Sb44)Sqvtb{n(l$%C$azUlD)Vp^B^}l@600X&2O!*=7wD9Mw$F{wNK5Ce#SRb*q*%P z6pS}c44h{<3@uM`wBmS$i{ZQ;HZo-4@IiRV%R>Y$y=ic>c6vXHha<}x>&N#FvhJH1 zK(+v5Irwg~F9IIU-+Msv z;Kuy*#7j;AJ-&0Tv1z6Ak~tt^+YPT*KY!`G9E0lN$$Nrune_6M*sl6fNb;XIL8LulYHj_rT{N-Nh zXYKPEV`qNTxtA4#2UCmm!ck&3m&dtZ1Y%i9j$W{m>?~j<*AvUOKsdj6?u_v`lt{4avntPKs`hn-4(4NW>1P=Jig&5y zF*pY>9PxDCa53D%M&=7Rr1OT|g)2c=8;5ilj$e+}Sifm-uy*==3lB${}Z|4Qc#9}eZOC*E)r=%tXRH8$^@!^8R88p>ZT{A_vC*BNJko_SdqiloP9 zvqIyF^Wfm+CR-%rm(ClmML51)tg-n^=MAZkE)(AH=?vEyUqYjH#e>^?SZMP@{6O%A zY|FreQ{O+lpRU2FUl@<(2>kq~`%Z+OAx z))kf&?EN0fvPor`t~HJ|lSH??;69Wwr!OJKhM^uDsT5k$mJlh*b=e~<-&e;V{*~p= z5zCG!xSN6Fk++N^e#!U!(K!B!SayVyx6k(_+qGju69xj?3*W@zw0hsI)BG- z3oJ1}OK)1pPVdclIFhWf;nMlL?I=VC0`^2T* zJP4$a7Byz--cydeI>bNq#NY910rKG1*tF95yK6)F%Ryk9ziVK(aY*A`ASqhZSif}s zj(fc=|Jf6NcPJ(|p6{}TRz8~*TKB^<k&WFg27bf(>kEkgN!qLaYLk#{)O0;6VnCM-rd_M}C2W z8+_k5{*hRAgz3byWf9ShMZZJ8-|;71qAa@&v-9c2lW_pq-{NI+p%9JSswtQPpd?jVdnXDV~jGoOO=PjGRbiQ&i=%wIqjjg|Q zzLNKp+x+#!SFQ%V@VS4|u;Hy8Owj%&iJ3DHll zQ)uPMUr~Sdqdu-B8l9iIn%FD9S$2HJ`xeVMfYHS`Z}Us_5ODbW>AXrhKiTtI;%H>Y z{T_boT4D-zaF2rlz&)-dmTkcWz`#A;ZyfPU?(uHpcq_5&NId}85^tnywwn(?!~Vy$ zM9z(Fg07*p#44csV=YlzvlBZw%mu=PS{>!liY){`}vt|@K+JC>UYl>aTGSk--dDg||kNkqAm(Kf9 zFG>pj*4VVtdB4wx^4AmZcNiu)>WH$2Rz8~*TKAC|@_y%M$os9%koTj$j!byJ2T^$( zGuDKPWSsxM|v@_t?IC-kRhFn{`Xu09Va(8_)Ic^sc z`}qrrKVdzYY|0Q||DdVBBd3NfD>xSpU90kDvG935j(a^>z1_>=4A1JEc}NUNLJ%HQ%VzB$~)bNoFgChMB+V1{=w`s0&m z=_j;MFtN+(!D`s|-}w~nEuHwt$N*m|%{jWeJ%=ozl}U3>GWE_?53VbcFw_W}YXN}w zzP!lbRIMKDL8OaOvfhbWc;V4T>W1@d@k#9c0%D(dP^M?f+#M&nPxwxb@TFa-dq2pD z_Kg!||K)_i^*0)R%i6hvxS9I)T!UO2;%6A&@-qj559P#Q4b!HXhR528Z^Oe8X^r(u zUxTEq_h|&`jSu}S2wY}~0a|)z!On@&nsP9Umd_)!^->MHjl-~+u0bvay-fH}u0a-% zuo|0IIv=_`lt0dKHh<}SXa(q{EHOY!Z$0ec^0tKX*ApLl1?Zh-i6}jyn?tl=b$DK7 zGA(Lsl>30gi)@3K_kd)w26;FNn8_OC3^Z0IeCT`>p8GPavH7#Og<<#q3w&t!8;Jdt zvTgqrYxvOM4bfTk=!b?U?QKnx>=Aj>I~3lp@KJ?NDeMF9%KIoX-|slFq$E?`wVVSS z!<_T_eqN*Oe1f`b&QSyvTwGb?Re?7LH~3WcrS6Ws+`}Ng!264&;2(Qw zU_2~YE~9HdHy?n8uQn`M@(w7gR3{5ul|{guGL$=j;mMq;o^f)~`(rUvn!OVXdbloS za&FFq{|xp-Zb1z8Jh|Eguvfm*Xb=vV&Tcr#56VAEegfJ)bGe5Ax;wscf2?3?#UJDv z`^Gi)U3N{KG~|oOZ*i3m~&eUSBI>!liYn+I4O>X$2|MU9!>_uLa1ULORD=F>}GzvRd35tbOBO)H&m zq%IuWZk(HJ{;mndA(wMNmJiBT24wdIOKGmiILHqkXnycLj4BQ+&XpYY zmajMv{2k{^-Jdmqu^&c5Qyc7;j!gN-^e(z>6)nd{j8tC=A7DL;4! zjmbYPqFeI{#@tS-}jKZb2B9&`U0EWI+LCS<@#u3Ci79@S$vLTee9HeFQcMa^} z{M`e3DY#i<{no=FoWJ%^{&L}G>3s%v#$o%>@P3V>ED_+e^4Y8qK+J3C5R)weXrtT* zJG@$~*HK95NQ;ERrR(bX2*PEuZn+$dmC3p#&(?QU&!Mb{)#jVXfmpX>TLvbaI_r%+ zCol&}C%ApJkWrPnS}*$KxHyn}>iI~Id}@`#8)$lwqIvn zwm-R^M45lT434A(h=-0jARuEdn{ob(7^q$t!{>GyZ~snD<#U%ZNLFMP0QlTXiDg^x zLtx-@Ij_M{mOZl~$6#Q%@o2OTu322)jNFDFu)N~|!168vaAPrP@G;i|$&SWa8KQnI ze&g=__;vH#d;l6|%w5uNE5U$D{+9RSg`sAF)Vn#_KT*}SBFUCKG9fY0ySw+y~vmZEjr_%WkRCUmJ+6JIxHOrL&Ysu1-y zIwehNN=G}Va#K?kdxqm; zPH?i|ef<0xXU=kB9(3v#wXJDvuc`)YZ#8hawSjz5d+lndl&q<0uX2j2+uE#7qN?iZ z*4j&)A_g%!DC?W*n!`92IcmfBzw8E`u6X5eM2kHpt6-+6prV0Q3%q@xu_ z_ZL&H&;o{~N#~Wj3rAkd#$h~$yNebz)^8ecYp2&ccsS~;v3}{iavbG&8i81jp4N0< z1A*<97@(z>&MR*Z)eGx~^+#A8iUX^|yV_VbYivEI^U5!QULFE5f3W)W(Bu6)!0NmX zdTb+WZ2r=D<<~>`1DoK}OXrm%Xuy;u259N6hdta6-VEihCtkS#^hzxerAKsgh&HUw zd{jgxyz&hQmkFV`GW3bV_houA=t0h!&f^Bn8RWK(7Ya?>;?;Bg@Q0pWk466|4Y8wl_k48qPp@lIbCFj(m|=cH{~GYzg4R>Ag4!zY%^*gBy1F+Q+JI zJ^&4~;4aAyt@>tHudKsD?;LuZlMS`?Kw5}k)pre{QLM?i(3Iy}uzk20UFmd|(XFYi zOJj8X)n5#WjMjbw{&qWJti!_;0J`RHZ8Ot9Ui2J@I-U;b~(-jIIoO{)F*ps#kcQ!Rd2umCV$4co0AczPvYr(>|D5o zjZ9o{Natg_3&*L<#vvVsTTP1^>o*O!wbN@G9*!<+tY11G%eCt*2-F)Ndn*WZMK7I? zoe|;_Ss%7us$sWzV4gBv^PL;w6FD7Qdg**D*L*YKW4VTG+l}*;rI*gf-VJ&w_*-N1 zp3cYCg!0!DANv&O9b$=qD6M=pD+Cbp?|30iwg{k&av$vQYO!8PWIPi-b^^j>!pByk zQZnIVsh{;A9~EzXhuAeJ5B3M@F&TqNUt zHXld?6OeGGjf6Qa8BZE!0h}rId$|2MZMfzCb2v`vHr;ca3I8a5ANIc9gSuFUhiL%p z9nZO}skSC1?A(6hGbL96?M|F!C**^(lUm8$@vD2XnOOX8bz0h6YwGLjz~LOz2v(vpmI zJ$)$dB2V8T&{=!D^9<_=4`wYH`S!iSIoKr|XgSe|43ZT&766>+E@Ighd=D7*3-E4m zIC9GJk4|xaJ-pteo4IlbTLm}?y0NUS1mEFLy6SjrnSg8ddHl7yHXvA zr2Qm*G`bb8W;4ouyLm{p;7T>=Mpj>Sd>{DA=aJ!$;Sr6x%soXM+7v{^*ybQ&RLA3* z!A=U~MFPHuyj_?vE;ag=y4opG;6W|3W)1t;kxeqwJ_(l@*IFL+u%l|;QOnA$^xr_WDZzU^&tW>y6VUt3hv4b0)&DMq4=8+G zVFMw;?**^fUfbwzW$l&MJRaM}GS$uW^k8HL(@mLxV13hZ&e<8=e&$XNzwmvO+}j$y ztNh*f-~WA1$(_5Mt+(uU@;YA0aXMZ>nBS9JkNaN3()uX&!{#OuCnvDKP2IC(2kfD) zJ0y|dxh`xA9S}O=6?fsoApP>OSGE}5{fC6YCW5e&G126h71sd_!h{ULTm(`b0Sq*d zfk-k>gJqdugObj%=oEvKh|IkNjx!`n_U8^Y+jNtf7II87hlCQz!X8UMTR?{S4fz$2=v$JP0jB`mi)WZOI`I;HkE07n=2on5yfOKPVuIRLEEZ4v8-u7{ zPGVjN%ge1;?RaBP6BIw}L3hutP;2V8YE4U>$G>s^KF* zFZ8~gThZcpcWcDlidM(FN8{yIta7}24H8dGC&~L5(JRPFj3fMt!E$pes_Pu@{yQY` zyu{s1_Nyk|u*8v!_aN)WD;S>m3&T7#L#U(@UnchLX2C`!zQFu_=aYgJCU!F3BVQ71 zY;MKMYR7xbq)-A3dH$Zs^JHMi^!H7AQxgBpl%DvC#G9IEApF6jf=$n@XaJQb4V4** zCz;+eCf?jcJ=1&EVC9K8bM|~8*if%vequhc7ao@~EOw%KjF>g}cu;j2VF*h%gy;~W zsBa$EiIOjJ;{&53fDEqb9GIvL{88A4GRDB@Z2S&p4YN;CPQz(Or_KBCI#X`_N8`!OZV1gu z^7={dWR07bJdQL@@ooht;S~%`<}&W7>RXb$kb$RpEUs5jmi!4Ta)$a&PBzf@bZ?8q zosvA2zOy_t1x-yZV!E?6@QmaZCNxKVXC_}};JMyI5_fL$4F*121D7YOh?aY76Et9w zEVS3^wMyW{N$EK&J&wpo!P8z+xRFad4z@_ab6#>Dqpk6H(QBmOH(5!Zx9~3aPDK4g z3U*{Ac_p8Bg~tt~k%B*Fp$S;qSz}n7i2D-A9I_H&Pl98Pm+dBHYK^;eV|C{;H%40x zhG()rqxE-9?Z-ik=?sh(;rEd5;%6iZp6iWtlUFfdflD`%IiF-kDH=<@L*Jv_=fpS1 ziPOSIW)07YCK){Y(ErND%DlJSOQu-Jg&to`r1o1zU!*k_P4XkgTkJ8kR}fFis65Z( zRPGgwN`8aze68)7N#4-tUEoEeddrg$#=X$HQgX65c^WA%@%ZZU3YI7NV)ZUkv?aNX zXoaGylQT)G((5l(yVgx|;oqzB4j24dH|f&9T2hV_bhycN%*j%T9x1rXO@52yY81cU zOJiPOUFC*(&sadt^If#SnBj^0F9J&a=&&Ga)= z;$f{U^u-?2bmDj6*U!l?=Wvc5cL;|!It@MlKYQ;2Cs$GBkKcQ{d#00~OeQ1)0TSrE zAQ6(8gdqtJF(d&J2snfU5DA@mCPU`MJV?S1F_Q3D1w>(8*7Y$cVis|iT@hd4;%gTl zDC+KSQE@jR#2+FmyXvZ|`}hC-o;v5=>YhnvG6|9Ollt`SZ#~bgs#~}2sXA4s#;5rP z9mm!iS|9UG@qZlu*%$D~wq@<&$4D5K9e*Nto$vC*^>E!3$PI(=Jl-L! zpMrhy=Syc+9~^%^)3h+nwUVY5X;zTjI!0^LXj2!l4o*x7fiO6WuArL#hv-c5zGm$E z8Rg;M{&x9&%(9Jgj}gZgA_wnjY3b~93oi@a7X0?ErcKTldyw4HOgwUQBWNAySN<^w zQ`PfQo95yF^o9+oiVJU!(gArQzCiohr_Uz<9ri0Kvv-AzM?*hOUY?9gXa{TY+yeBardf+%tH>IjKuf8m5WV- zLig#&Qb!{aB{|*PTtFyqI z7F^kzy1F*CF;ee__WqV+;1gIVSPzqzFccUjuDfwQ@V4GkR~4H5=h zQ0VNyzN)S!DM#@d*aGT$V=j)qnzXkzH*6(QDR$xfm8;H^2Z)&$u6ss+d3jF|9Z^td>ug4q_xJRm$6$Rl z@cqK0XldvK%UkGZ*rLq@;dsU^EzQAZ+*OD+b@kH|`|;T37>L?rZZ=D9TA|W}@z5$d zy8@T@#`aCF;YfwFp+ZXdn+oj>*c^x#S~@LMRx>hJSi7pLa|7B_3pP%oIMT>0 z$4&6r(A9&o_jmU9wlqs801Dh0$QIev4|?)S(8c?;FRW{D4FWBt(Av*RXk|k{R<%DW zbV@ZkiXdZMmXxinx0!jAs!AG{6h}(UUdR>dh+ty0f{CHly!amjeE-F8&KsYR*gS&G z0{rf9P7a@~c>cXqF2Oa7zF}ONQ*hxU#kpoN`-;c!o26ky#_RMv%(}EB-b)DJm(YyG zE0s$;#d4sKEC;zPmY4Zjjmy%&8oU8>2IRcVnDr8Pc()05p`w_c^oT~U5q3)hYp@L; zaDV>Y>iHUk`Se}{y*&I#Z!9jO$N40m-lsv2b!x`KO7{z9A^o)ck%usPx4~{{NMjEI z^R%e3up{ zGC#yR#j_NbD)K#N_&UXniaQkFq4-|Kor<4Q{G#Hw6u+;ySMjHcSv)G<=V-;5idQK% zDt0U0qn60RPl?7GP8mBFRS~%6|;DOd7onx&ry6v z@q3CdD*jrr3~h$#sugD{o~F1|@lwV0id~8~Dc-60KE;nIepd0Tiq9&(sQ4SjEIvle z$7IDC#WyIfP`pC1RqpOsF4>L%% zNV%fu$bo(w>d83}9XY<(Fe9QiDAI9)y0T)SA9Q{XElJB3OVY-au5@$Sb4(f-{FXK6 z3WBc)U&0PdX=JWXL>*x)tQfHR&J04Bw_Y1l}@by*HoLKmTaO!)8gbXvwiBA4FEm@;`Bkj9~!|57a}qZ~DlUO=*bPTTN&Yw5GuDhp2om9BfRHRwQwS1ZPa#m3{X;Wb6d2NErPQ=;&d={(K&SaGL zkOYGN!6i?UubKGp8?P-&-27vFGs)&-JpDqGyr66dg?{GJO>e}LKJ<4nVy^>VvL~f4 znJf@;@)u7c3|eX@`E$7%`Sy;2yW2h{Fo$`3=&vx*;I!#t!dTC${eu++CHZ5wbp09!1r#A`o_`PQS*t;9O(lPTCR3!TrGnT*7G4o6>e;iL3 zz1v{7G^DW@^zyW*v9RrM`}sT7%U>nJj9%%O`AX1RY(x-`R$lij0>s=60*lQ7v?LF~ z4ieoK^c3QqU=A-HhEH*uV(^a;@3XY1F?vMT54(UW6pD))_^lm}i{)Uwv#hvpZ1Q*r zL)CK}K`nM4IuFN~na?sY{@s~>ww%NqiZL?lh=zRy$H1&NGpsk66cy&ODZnV$b^F@; z^SwBnYx%Ci{kUeYL2;uZ-wpbc`y$?}_#wqd6u+$aEg~AtcNJe${}ie_=EKooSSQ2q z-Tj@SRNn0%8J6!peZh$LkF00IMU$%&NY?Zn4Bu_+@P2fwdmEMaao)~HkRkWNxg~1$ zk&%v!?bBH4YnlB_>S_R3>3^lG+<-*DO8?$@;vcMZI$Z#!evLl_y+FV6)RO?7x3gA! z!)X8--V_SdKS9v*cFMy~(^bJJAzSA4xL&sj3Vv%01|VK5J#35yG(_fObHa*GGSlg3=9h<}_|X3#z2NP}3@k@KTR*v8SZ zjlpR7Mt)HFmXV)9w;A&bhl6kI!2Qhd-ZV>bnVs&>;Nn|l#==VZ#w^OQ6OmYs{qc>Q zmLryPe@5noGIC;nQ$RByb4T_zL!X?1}ZFHlJ-lBMihQC)) z;v<~z;RyE@Z*FMovnknO_j@cjeL-Db%=)o!J#W?d*c*X6ITNC%NL)d%-gOp-lsDYh zL~Ev4E4inW(J37-X9LI@XX(^fO0J8!!;|0_{w@BDJ7ix4?r=N++~Ib*$_+Sn1`GHb z=ZSw#w6X($=U>oOo@xhRqV+*~vRe$Z;WPjZGlZkL?k ziY*~=@GPv>w&bNK8%H?9QF4Z(-yi(PJd2V1jv4DSm%_vB4Er1|zENf@tduw8S&Sp$ z4L5?o79$2UdY8cN^Irbh*ZboQcY(l2c*93M-fMrnA@`PW-q(!fuar0B*@?^-zj62^ z?vpp10eX3an=yLq#r^Vr!^>YK!i?7}ol{%}dSi_U;?c_Meno(oi^0n+HV4p>l=6n$ zwlEUj@WY6AsCh$<>CIUF%x&oV9}eCSQ@ft=@BZrWhJ4mE{1THloG32$XS|{I*We80 zi{id3h{E=FIQhb>aKEAE6oogyOYl~PqkXb%6W^}!?^7frLVvz9BjpRZNZ1&|Vdo4N zjeH+4gyJ-ZlrJoj)$fZ*!~zxDh0FB|L(d$JF(z>=nZe*+aG~B{iaEu1z$fJFzj2B; zGfF1K2LevG&%F^=XrTEvp^<;3 zD!PjfW^@8}iF|39Q_)RMEL>e4zL_|F8voI3$E5!iwz0v}_>bhQ-@_zsALVutS*wmk z>!>ZPjoy!{Zuu7MYySt|@xk))@4YmIGi|Ray5TU%anQ>nju|U2zwti*dZU-W{qe8Qf!?u31T<*nb-yA&%&+i;8VUc(z3wC7 zUoS$uk?^luP$?skiQkUG4^_{We?zaRcxM0c2lFSUidrh&@>!0fK*2<8ckvz5VxB2! zspew(tUSxa$G`il!@rK#n;HK)luX>do@8L%rY{F+eR`<$JREyKxRAyQS58js~V{-wprE_Q3DB^!l4& zzjrV5fVT2d5De^j8TxX{@&mh5@JrW1j}GGgH|S?;-?KMei}-oa290#>tzyr@o~0dh zxQ-V2-yN*qvG@C+Go;QP)vc3Cj-5=Cp@!_)`06rGYYqIVYsXWKlPz9ZJH}`1$L@=< z&lS)9R;a=nsMwJcD2orItUL)1ZG56)r^>0=3HtEHqGiE2PXA@GyT9qokYr{Wm5keaD>^iM*)9_ zt0J0KzNuwv5KZrf3kz_w|0u|CC`HIV>AA8&CjAz;$J8W4A$^e{!I%hM4(|w z!gaZJ!c>WHMNTF+rczx?cxi4HVQLHiFRyq%qfOsL$C`0C-f{+21%zwIU1!$I$Mu+X z^|+=EM49<|rm07ERw8a?Ce1sK7kUh-;yvXLG3sH@q)9WAmaoDt#E62A^8XO+d&id6 zri}-;>KE%AwC8Xm4X1;`w6}|NF(qv%GTBUEyuft*KkmJ2EROCm$!3g1=x3YU(RM%@4hX z9Ho$onFC*yyN|&@;?4FwXi!4W>)Bc*-f$y_>wCJo`nm)6A4isn^-i2h=`9uM5#d|A z{7ZNon|B=LkV_t6P4Za5xp!mlv(+oJfX%f>=HUtGiDt$jzKwgArg5L ze1GF|l#XLIAfqoJ+>FsH9mm{(PQ&Vj$oeBT;<7Zb2CERjvYD}Zz6KutIN@H<8_784 zMi3Z^uP3kO(|f?nAFOfyZiC&=-+MtX4?i;&wjCZmy@$R0?Qb0OSb1JQ~M%Vy?q zjAM{3&-iy|{!uv39E!EQeAYBCi;vv-zjEM z{mhrJe~8b%WrX7%Oiy)f9qIi2Y4aB?80or>W_E69)^!4I&2O!YdoZR5I4-H*!}Y&m z{lIRtC1{9pJ6M4Rx3VaIO?*{Fi?Gtpl#Bp!Cu!V|-eb@0Tn<4i(%#5RdxhYQ27;}*Khb=YwV zQs}UQVu``Sjw!s)3~6^5cF3M;b;ZMu^BKhrJAMV)+#ed|VF@abkGU%6S+!%$>jCFKaHETay}h;k+934YINduYS?Efpa}(SVzk% zEA9$!iFTywE2|inhW@nk+u~Z*1*p_ZV)i^Ai{5a2R?{km)zQI(f zUAA?fmz;tRPZoZYaG~MHyy+};8y~h4Zr?C2%_+E;ezN1ZjV?AKN{`O_)eaD|CNr`z#T|$@k}<_Yi1%3|4zw%~UEl2j z{~3kK!;@nQmWTC1WLZ%avH2w)!b!0!q33yt)$T*b;TTi!S<-C8g-=`2{=L#%z8Yf; z|GUY0o2q$XzBuNHKaCh<#NGPLhCoIcacAz{i4l)C#w9v(rnB5|&rz&Zl<@}q&r|m* zMHz3v|0;DiC|;{5;|+x0tZo@^!2O`QKd1OL#b=0UQ0ya!KT!V{6}i`#`4c`5;XDU} z@usQ&JVjAbig=$?I()BuT_92}w81(JXP-5cE)XcGgObLM{*jJP7St^q@t(vgz8q-Q z%tL?`ibw0pu1A*7{qf}B?iV>Wp=>4`z<32?k`%@u%k%Giz9M+{iyVt^+!8FI|7|ah z)$vJK3%~rpZjNDW%))TWRCs2=Bg;)TBR9q0%q4^6Bm0tHq>2|NrHjQGnM9USW>_&7C%>-NhF&wHH8=<7J8QMZeJ@M?cZ`ys)!I-su6slj3%ugS( z`RT=YzW%c&KSWc?OLBcRzaQ-D&<(~s$t-o-_jf1UzF}ONgW)ANIui14^p?V9b_Q^+ z(Y0oY7Kfb!C;0<2Q{cD~4whqoyyT6b_ZCY8w0o8El6|1ZIwL(IyIo=cX7 zw5W02^WZ&%H-cUj;*cIbAu+vDUh-7D(0PQLv3r&Bl6QFdt3YJSUnwuSY9I7UdC7ad z{OymIYzMt}S>}MuAFulr0b>3K@z@5;K(^HZ?XD8d@3}+X~H4@*F$b7I*ai+m_)Q-|X!A~GiY z7RD58?<3LOcrxRGN8r2~`MrgT7pi}Y;wJUyyXo_L`{M8VTA{Mpn7e#__;ok6XN-9M zc_VL%*SSu>@ps+2?j~4|H8Zu0<797B71l ze-f&zDp~e2L6^PAX(CdSj(cntyZZ}&0*r3LAC|22@sWR}UYm#-rrnSXvWqhZ<0F2P6TKb`%)=Zfc_Wf0`% zpLr5VcpV~z`S2nD4NGFnUZzNd%X5#=Z^~T8zBI>~@o>rt{;#e06ho$6LdP01%|Rtr zxtu(kzV)1*=9-%EnKX%4NfHK4AX)iCjMHGxq}Ry5^uu34j41dB|6fxpTjbr`--Pu1 zQ!bfX|M>T7Qa4=amaj;E;;&zsj6#`VJuk1Ux+}ajx+S$EExZ!C5|}rmE?5cD8;DQB zkEg}WKflE>d^xVGOZ*RfGCG-Ij^}gPf)j7L_PwITWm#u$2U95W8Df?EL9BH-xM^&C zwz|0G@Q)U7Fa;LF%kc;E8hhil@(qoRJuTP!i|N}sTf2NW7W=gL?$(|LC?K5I)6%ZX z?YsLMJK+VAiC@Wz{8wjvKF0%S$ntFk$lQkCX00Vp(|BOR#N4N7KpHgs=7P0~=k4|t z?^MLGG_VGYN4c>X3nQOzc7}0onQxvMvz`eLeD`AWdwd{Ygp1$w3@(;~`68chX4C+J>0JZ6rGYiL7Zt>^nX&r23LZYamq8CKj^vO2mOt|O zKD{SFkNt)j3)=(_pWg4i{J|R2+YY;>A&uuiFHega3wtZvKD{)UEXyCi7?!_x!)|Fv z;`gBE_m>tnpz&%M31$)q@JJvtwDS6xqWNEhhqc%oUKqcXxK+0WZAZKl%;CiYyBN1U z2H%f(U#3NkWr67WaTo9`3eU0TaN`C{nt{(vjL}PcoXz(ie%#>ag!xBFJv01z{qmw` zSNw!X&wsLXvxOukC0gNqH%A?bNl8oa-K^!rq);Q|$!VH#xjAR;tTE4QUr4gH2cmm@ zI^(19u?-OyDY8$X`#eRy({ytZkl3i$rpQJ^e_^SBH>;a{4*efg{G8&~6^C5Mg7`nv z@ZTxQIu`iL@m8pazo$PQDf@~Mt!II5KRL`q#}vcuN3C02H{x~t%iW`o#}mh5#&{CX zna$s`w+dJlz>f9xDXiV+`g``7W~wmmI2CbDeNy9NZu!h51AFpSAKaT~+NwLTAK*?| zJ70x);wl|eysQLB>q@)`gXfBqNnfs|*JbtT;R1>+fyl>9VtxYJK4X2^F?7v#t{cnFrE3m*snm@7!m_97e4TT? zFD$EJj!tpTfiTn1fkRoH>wz%S4cA=f+8LJNi3ia<=X^NiYZ*i}%t30#BVpNdbk@4i zU18aG>74JJFN8t)7~F@hEG@Yp9%!%NM{NM=T3+;V0k9PiCY#lE_l*O1TJjo49;(93@*k>?sL0)fLntL`#W*92j}*0 z2rg^s4&K<*7hKYHeXty-m?3_D0}f(BnjW#BNJ!hX5s36{?Leew>m-e>M9}HSVM?Th zL*W`*df*3|U9^F&d69l>zYKY}7;ShBj-0vzr%|oh*dLtV(-vHYL&g!tGng1Ih&a8p zAYP}l1&rS#p12p%Nj#+O?-3jH9n zXT6DW&P5BU$2COwRgBVskJ7A_j%I#Vcd%+%Va>{m&tuY2c$aCd!&BVoq zdMYk{hsc{O$F&Mq6|Ob7Y&^&M-GXZyt~+q?`|>_q1GpZ*wG-FFxE{gvD6YqGeHqs` za6OCb1za!V`VB7jDs1wzadC8B%M1RCaV^KS3fFM=XMeK_R|Vqw<6FATuxyjnLHi0^ z%PX_>m6PuZcc6n?2Dj_y{solyN*8zxSbSa?o3b5R_C^U+pN3}hCUsV)c9(RD={5_n z0SDlrTkPAIC{O1Qb~Da1W25lyA_s@L)6G2J;L1X4M_=Gba3n=Jl*?L@k~yIC4GM5pov? z*Q7mvUY#2Y_lTE2E*{O`859qGJG*>KP$lxg1lHm58*v(L8jh)bj(gkqAXoxFe$DZU z#C^^zbsM+;0NlP|T$)pGWpVlOUSWV5yKgPrmWDy3_s_Jbv9P6Zo1H=2|G~H0j9JNN z`{j5N<(P~>mVNfvN z5$^UjIu(`sBs}@OIu;kp!E&*zKE0PKgW%opGh_73 zZRq_~5_t5)hu#1O1CKbncj6|FI342CCh zLODx~X`M+Iokz#oV*fSUf;Ihm&&_^;{D(88wfA~w3@8wx=qW*Ub0A{KpVJ&wU0K)O#}W(fA7P{5W- zp3s_b6w273s`AKQ1e{4kgRBQm!G~bI`Zp_XRAgDBtm{XIofA1)*V+N+FddNJ9^!?H zS1PtD_9<>x10AIqy8CAXO$g;W7zD7HtLnoVfY_aaKVd>ngc?n0QHnUi}1F!MU;uV)06 zI95J$ild*-J;NY%Wpf+TiK`-ao)=bCS=bU}PCXA)qZyNe@#$T-SC(D=H(`+yfz#au zcN0?0%*@YiLjE!f+|B#`;~4V{=bDmxm|*&Oqdxr!B(nsGZ8aivK&`yTw=hf9f12*;=CHSVAAN7+R-(~=v*47JjS_u$FM znGJdmkYR&LRaO=nTZ7EQACQ|@{03VXH3c6!*q|a3ZfbL z+&zr>sV;HN%$>+PeCD0vnw=ZZbf5i*xK6Ds^fv~XC)^!s;Xr*<3dd2+#gXZ> z$k)O?i7CdXFU0>*tby=pWWdoCo6_xPIO`<2FH-$EJhCif+;5;oe%*Ityu-5aU@vq| zPgnmXi?=8uRWz6Ct?!8TtI^5lMS?^(;nS8M!WKNp&kDU#O9n zS2r>AA`M+peNhPKs^~FEyQ=z!jQmE8TwncL7NI`kGr9IY^fRFh^lvx*w*MNL{H6lE>rz=hTjyOiTX*+*p;cyGyJX5KTG(_ z89V~kc7`8`Oe%a9v5)RY+z%mIMW#Hgmaa7$(vQ{sKTJMG9wD?BRdb3qGd6U!p9L}A zXMB1d{*Qhfe;LrO%uElf|G?^+5z>!j9%QMX0ii&STY-;mIXAWI7`={2Gt{Uc;6 zDnI5IScULRW@A+SI#Rnl;|F?Y6Lb3*8M7_RFK{Zx5@v2Pe}gcTUY12&;QYtx1xnW;_s9=NR6s?uWzbolM`N z?nlGwzu@g!)%|!_{WGTDpzbe+L6(K!%P7@$Y>@sasx4cdWd>sJ@*w*<-+fGQ%xbt_ z$y^szf1Wq&is)vwE@XbXqegLV0WSaxJ1!wVHOR6_@)4+eGT)=;VI1y|Q$7R{nS-G2#*YpH>qcpi6AyWD%=Sm01uR+9cH)4b}A+!^SsmFh~ zZVIGrb0v7q)XuKWL0J1=2#>&%j88M>2{*G|PE0Y|VHmsWteMwx1AU3&FvBF?9Fgydq!>ctfoPWW* z`46OSm{(hKEyksICg{6x`SKr*z0B}2L*BEBOB^Zh;hdw83fj;p*n;E{F#41@NbX24 zndT4+{tAWO=Cf8_%IrT@Z%wW*T~ZLp$V)NJQ<=$ypkOo|fKzNQ*WFg|61tUWkJWTtaUb$WX-3zkLVE+GqJ@@@ zmYxkQolRSVLeuqqC<>x6JB3wmTy{}m^?A$IuD)#L<>wXFz`w1lu@~7rf90z4SZ=w4 z-2f6Z5&pFF^dL%KN1>w)q+E*54xGc=)l{@xv%syH?QP6LYbU89iO9TIKd6|Jm7a*y z(6a&Y+B&-Xds+(JkaiRrdZCTLY7+-JOAVveunZq$NwKZB8F!FHqa6S2qnQtLkTCBd z!m)X*bI5<%`f8EuHpsOKvQiS6llBYG?yg>FAvDO)6#&A2B`MVF2qHHYxJrvj?2;)Ine4{Q|am~utWu^UKUkbVsUJ= z4QUlkU0s{nQ0_)Z+0bIzyG(sTVM9YlM}ss}*aDou)Zg3ET)>h}Fxog34 zfo>;S1!cSxd>MF?rPvV2x22#K#pB7#C=`zF@`pu!N`=V8`M5a!ccj4v(AMS4V| z*9g0%ffeKbq1PH;gCL*Y640}H#;jaSuNQWo9_4F%MrJIm3m!hbdM|(EEi8Yx!ER|t z;{wpj)1t=0w!`hyyVA>FCBlr}dthf8K0lfbp!Wp^s$m$f`xVtYn0?gH^2vt~0d1w* z;;l!#6U+f5Et*pK3T+mL)Jva#D@R;?@tP)vPYoo9R&w|O~tf3jB zXKq8^|8U4B+13(638Y(RTyvXI8j{gkM%gC#4X=0upJQm zLHs9QXUg+v>1xHN2a{kvM-=3d-u4qlc&2<;hn2md%}r%I;2gy|#j_OIZ!nziF>#&Z zM#UY9?@;8VFXQc0{FLGs6~CqUeML@wG5${#vv?GAbHycbrXnXy>26f)R=i2^4#oE> z?o@nC5yNIl{~g7>ia%52UT4zhq#^M*#d8!dP+Y6X`6tG^PVvKvA5;8{;x`q4s91rQ zMd&LoR9vpOT5-K%r{ax@k1Bpq@!u3*R{U>8sC{7f*@lM6}DSk}xvx;9;d{*&A#os7q(SVun$%-|KZ%|yJc!grC z;;oABRQ!PA#}%Je{GsB@ioa7F%gH3%_gKZ*ii;G_S6r*utk|o#UGW~p4=a8`@k@%| zRotVPMhC_F)+sJiyixIX#l4CzEB;O~g^rl%Cn(NRtR#WKqDuDN(cu^$-nNz*f#-u3<$xQ?GXD?{jY zIeW>G|Cx5PUC-9hcZo}hMztlJNR?mZ~tSn7E(9hKNOyqCK3gW6lFe`=T-PVd)zPGSA4;h{u%@J8#GxBAgQ7rSkoHvMNo>Z|N70EoKSmyb0-aM9hp2l>s%#+8WN3qQF=X`Fl z%#*U6D3*CnW>}HTGb)mKMny8uD3*DS<^5urCp2XRQIX6uie;V)c)wWYDU4k#^SqgN zk7b_1R3$RcHWp%dnde;4e{IP;@#R7n6g^#i5{sV5Jike&Nj1~ePx{VmwQSR}(2D9$ zvh<0}vzAXgk$L_V(^ zCNj?xnKqGm?qJ$P=J{2IdNNO@^<__JLPDWRmX9Sri z7Y9U%%#-5wD3N*o4Pz%VPkdg{O~o?L-K>B_=6Nnd6PYJv$5A5l+{tVwGEdHSM2XDv zYjh?u&t1%hC-eLeX?Zfw4u*R&&yO%Cp3L)eB%+b?Woe+-x3nL!mTo8`liHLn2y+Kvk znYkUHSU!P*PexpY>S2}kp3R*tVxw5$%K%{(|>&h^c9-z z9^Gc4yY_^sOx!H7FFRp3BQ#&gq^sRc+L)F%xrOoC#f87|X-a#?r&+1fe~d~BPmrK6 z_+W@C2xNeCXav1(u6}ePs1ZndM17K1v*8tB}jHC2{vga z#bXpDB~sW?hz?_sWQiE4RD6}_&M6j@#qwzpJ(b9Hiv?8_c~Lah%|~=Y7dF}^L^;^< z74memOHm<|6BjbINHU$Ayh!p0`L&ZQs$4Rb*@6n>TDTo*&B?V?P~ z0x*-I50Z%&ixw$`hCEY*u)G~*&~Yh|11G^W!C{HC(+HE%t@;FSj=6!fbacNG$ z#rXKriy2Tfuy`Drnw?<-xc<)bFzZtIn;q8RQha&&r8Z+>r84Vgl;b`WlI6g(eq4^# zuv;3^_**a$AND-VN?Dg*FTaN%mE|HmBI}RXh|AKz8mz(8@p4+!SUs1@tkZZvdBh<- z7RTt7%B=4LJ+5OkWA`eRSK6xpS46DJ>J_f zXD;y5Xw3ES98PAPMSEE`1U=77tfX(|aLBCvXTqB0v)yPJkeZGis7LR`fcK%0S&vup zWO^yHHeQoc@V`ke~2JzcP{CD6O4ygmmxtD<4 z6bR8-9}(IE-}58#?jU#!jg)f0Bl2!n4@czPt{(o><=r#US+d=qs(7a2TE!;C>lELr zc#q--i1>JXNbxcC|F+^!6#q-Z|68#V-7fEYyy8N|OBMf&?<44bGA=AMdW?kgCiu59jakic9OxlNu+WbUbv$lRYps+p^^r;}bAOp(vCRD)q*5$%pM%?E!_>0eZ04m% z=AKGq?kOfrWbRGO+b}Zszr>yI4pVDg&BikKIn3S_F0<*}Uz1QQb7x*s2PAXna)WHD zB{zvFW10JO7Nu0?eiLt;$lSS+_v)0$+*7{HJ+)Kw#(l=ASmu5;ZxGAe&tr8I$=p-1 z%>9$Rc`S2Z#B{OD{V6_;SmwTku2|;IWq+wy=Kd?DE0VdVie&DoBAI(Cmbq6lT`Y5d z3-ehdb5F%GckUHW#WHtpd`rbL_v4wfSmwTgu0-bkJe(ttx!(!;uPvE-J#rMJQ%_gl z%AzMS_cyVYOsbx)zQ}iGs~=^}SZGD{Or}j_?i}Q%5}7-t`Kd(aK97+Tnfv{$$VBFT z4nq@}`x=HOGIuU_O(in-&+|r!%>7|TPGs)CXXHfYegmC}%)OD36PY{T%#-9 zWbQAqx)Pat2h$}o_X=iuKQebJ$1s_D3NrV_jGxHd|BJ*EnLF41rV^R^dl)&9xpSli z9TwA&vuMVO>c3@ZB6B~QX%m_IF{GTx+{qQD5}EsV=uBkp>q*L!xt~Q^p3MDNhI=yi zzhX{2nfsSW&Xc*no#CF${U(NcGWR>_-k;3<1BftEnR^N{_djEX6Pf#WdH(~IxudBb z%XbxzNo4NZS?E_w=KgCG5Hj~(62z|xW z*6tuYLn2>3wFP4CX^gXGYHJXlwFo&5*G{EKDm+`pBk>mFY%54|qx1>e5g&W!PYgN9 z6J8|u;(f?wh3oR1#!AIok!2N4a1Hzp(N<*6^LLF>{G<}TU#@?c0*0g7wmYIU~}Us zgJjV&nN$s*A+X2GW>&> zj4ef75``0QE;910#&~i%eMz*XgVf90?f54mk>0l)+N-^Tmqz=$ZHAFYk3#;e;$yT! z*)BVruo)#g2r=`a6cJOVXB)5G_V7}X@JM9A$(Ha#BLYsgsvl~>Z@gJ~WFp`FOL|8q z@0aeZ#wLa!@pqe(MmPsH;hYDDhJ+IJTmi^ zhUpmZ{ho(emzKo47~eg9Ai3HzJ5<)=Nt0 z4Pdh*>w)x$M(-NfnPvelnr6%gGt3O_48Km%{5K-r3UdH0O$Y3LIdGD{SOhCHwVz@y&HC)-ZP+A1wZDGGDEAE zkHF3}e9kog1A5$`V1{A5_}q;)!#;J?(DLHXAPj6-H{;O!9Pv(YR{1v?rF)#mVV?V| zL&Rfz$3nTlZtPvY8=gnQ$*FN>mE~etg&hQnLA$!x>_EG(=OtF(|8U5Q{rgiM%(jx4 zP-OWVV-L3nn!KkoE_{nQOG=e0p*H-vN_+~5;W!8}lYOqSLR>-{8i0O>B3 zFP{NF-dlFMAT_n4KnXh_e)A{aIHx^!Q*QT2D&{P6dH2=9fL4Goa1U`V{=J zADx26&UbeT8Vl&cIME5#Hxi8#1)cp>{_bTpGls(vp_eFJoOY!@Pdli4Gm_@_oeU4V-6c639 z2OHR^Zca+kU-n=FWvdQQ_Fw~dYPiUx;pR*U?7;@g z9&DiO!3N46Y@qDH2Ff06pzOg0${uW>?7;@g9&DiO!3N46Y@qDH2Ff06pzOg0${uW> z?7;@g9&BJ14VdMYJ=j3mgAJ5D*uWJUE_<-ymOa=&*@F#~J=j3mgAJ5D*g)BX4HTI) zQ1)O0We+w`_Fw~L4>nNtU;||jHc<9p17#03Q1)O0We+w`_Fw~L4>nNtU;||jHc<9p z17#03Q1)O0We+w`_Fw~L4>nNtU;||jHc<9p17#03@b`H4Sx@EoQ1HFZDQ+afzgOLx z6>n01ii_g@0N+OG50L(BzK3uxA;K;F2>uK~Qv6*P7K?_5)f;`r{P_zg86Jsj_)2^W zVTxtL=gs}`B*=wp2j0K8ZvFfB21^EZhxOQdy$v$nZpw#)mqhOS@GvzGN4_Dyal`N+?&QZZ~vq^ud8bRyt)i|KpsQn@u&##s<2m_IY}MO-r`SQ@Mk;gC3e@28s6n z3{lX{utne=;8UDK&e#a$qpjQNkH&{kx6@M$1kwKUhz`;Iml0s1{e?(vqJ35$$s? zKeyALz))YbZ(HWmIoUxQi}pW{5?WYAjzVs8jeI-(eB6}V>A#0+O19IFXBJ}7KA&q8 zi}ruYuvoPJ3eqRr>HjZXv1orcil1z!zl^R}wEtaFDHiQd!);_cJr=tL``k`Hp9vGu z{+pS%VMP1CMXqE!z0_)2|`PShU~E>L?QJC)?>i%6g1N`!!4#i}q(T53y+fTYPS@X#WQci$(hv zGOS3nKeFxgWxQW3+W!jkStQy|w$qm}EEes*jp<_1emCzPi}t@nS0dV9$&3!aojwHp z*OqAi31nQh(+eY>i1u$|O_=aLEgHpRXSR9_OK+hS)$d~I6Vd+b__Pzz{u+`>MEhK< z6D6Ykkj_N3|1mlf(f(iaMu}*DBcDhj+JA&MN<{lIR!KzrmojZ4+TY5wiD>`x3{6D) zd^7#+^czUp6YUSM5T0nC%TxUA^i${_EZV=8#qmV@Zvz?GPA?rmBHF*6w@*a-%}ll* z(f)MgPPWreX0{U1{#QvR5$&JJ(EW(^-@@qLcKTMvPel7O=}biX?_=ylwEu4mO+@?0 zGBgqG%lIr2?QddeBHG`<@+G2u8T2Qj{jam`6VX1`5cu2axrWQ%PJaQzJ<+}lsXfvD z<0R*a_Wz8J$`kE(Gu#vHzm@L&iT3YCgprE&xt;#U%y1&wmjx9ED%$6EdcLc+ot_Hn zU$yP@ub_Zo+40nQ`AJ%P!f61S0bC}|-z4JvHB);~pW&uO$Pgs=4-qys^Z&}Jrz6Aa zxMJ#OB!q;*tET>gICz8b@)Ksl?+#oiLdp0RiLe@zqiNdmpJSBYGtpfkZ!rLC6>o?& z1wr@@$->$xy|79KE}bU1d8aroKe4;5ISB6-lGg*p zD=)fe?xi8#$GS-rEi%$v2v&1q8B$Z&RPzOMB=J=F{Vmsd_% zURirr`1a`b)NScoGq;rODCannW&jt7EU&Cu!Si=-jc!TpK!dVyw%aEqd{VMT;_Avg z=SpeV;${P}um3J)mwBOKo8owUDK1nN%$;a;U3kNi8D73`1)Kn_6TW<9)D^H{yJ-|j z%iK6B$eJ3qSTp{jiju_?uicIPo4a~8;e=k~w@7dM(EI!MznVg4%$w zzt0W)!!Fv;7mFuLE=ZQE$gcPOY~R7=O0;|;Z|@oKqbM3mfm2Va~_(-8QeR|?-=LwX(;pa zzclv!DP5cKEQE#kAueU@OwV`_KLoSXEldLJ%e}7_oD!FK3NFSUE?zC-kiHqxd&=`L z>rzB8JL7WYHNU`SEUa{GMmGfapGI7kgLPo#0Q*ry?=P{(yx)j;VWn#`X3uctB0ZMR z=v|{}7656gsxTwxtn$xzBwxBVqYm-jR}zoT4qRX-VpEIz*2Bg7V%|Q!@6E7#*$$oo zz4PH`#=>rbhfi;tR}P4PVtS=(GmeJvJa5E+M(^FQ`}A%HJ=|LIx4*R+3qdc+Bm_y1 z*CUD+vKaF2#pdwBO4nv|BOV@cY%G72`KMxEoTZL4a8KY-zL*M_8u{OhAkpW~VLQS(?5);ZAfu#Mp@PRmj3(v-daN*>k zopIrux;HoJlZQ4p`RZSKZibVWcE+Wzh#x>4Ai=N3S`a60ba&@-%{#07)6IU%4Be;U z8oZH@3vk((g5m5}%rN{-<^a;&RN}rCZX0tjeNRa^-&-4dFkD9w7}F`c zJyR6-JH1a_T+-X!11U-?t+w98@kjK5!X6CkX-gPad}riPCU=Aq!VsyY^ZIz`;`7kS z5Qcr*6uclyi1>J{Ct_H`w!}3mpQalxCfg3(GNBGU8}2E2$n+JjBu>LPl!*8b6Q}DM z4#elTfa$-X_zG|;K5A@J5i*fQ+nI`Yk!^@>_VMgrWLj)rYhlm`kR*tJa};GQ4BWC7 z2DnuHWi1Tc>(sqbafjkN6yK}3Q}I)ZUsRN}Frf2&b?;UDsiM>q!euQCa3;$Kl(jHG zSqlT~R)1Lw1NR;3mWeyKcdA>~!oV$JHQ;yDU)I9FEo)(bvK9s?Yhi%076vG5VSut0 z1}JM`fK9At;B|^0R+P0c@c)duWi1TcvK9s?^UFY43j>t3FhE%g1C+He!1d^4Nnh5& z0A(!0A(!0A(!0A(!0A(!i>-5@6~?{KCry+B*l{z7Z7nD(bE82r~Vt&zg_XI z>Mx>7#N+ppdS*D+#PsBW=ssKBSE~DJb#GDkjq1Kz-S?_n`a}E~@*70DeO=JW*VYy< z>KRVo!-Cp{wRtha>wBnkj8eR&ry5^Q2nL?#`kBFNIrgQ|KUJ*JKb@SF5UgS$aH092 zTXT#!k)3sj1`$LnXu}nRCxx`rFd$r)!ffa)39b*)ZoPqgbfZA3^w4NxZKAHxq>^O< zHl9;GDXhU0z+`CYJg3byT4u?Ax?V^Li_fl{i z&W6kPm8K4t|5Wk062sQOc zx8Y*zG-F|B!UJ7oZ0-dw#A@P) zy9Rbk1FP_vd0Nz1{appOPwxc~Vc$r4^k+QM@51HN+Xx5P)Yw?qCiwXDe(B}UAFppm zJWE3&x5AO9MU90~km}R>y_Y|Kyv}hr)6{DE2jSSJb^^nAy9gccRTB_ZN;pHW}6a2d~+<#(p^T{V>_`M9QiNvHZTCY?m z+oBu)>4#gi+gTq!!f_8iFf#4|o(y^sZ~+n3vQV*2kY3zZNgbA@8{sP7fWPcrOi?bnC?k1850S{)u_>ct5Ntbs(gK9b5t7BK z)sOw4%6`$@isZU~pOgoL{swu*zAf zpQlBQg^{oF=~cjC^+Ggy>|`trNpOBGPm3C>=WF2h=}m%z-vH(hL)e&JFYG=&%9{8r z%~%-gz^6yOik3h23zomzV7D}+Q3HB;TGUwBcDQ|dr+WG0H`M682X?06^P_nq=-uP2 z^3O0{_bZAg{+>s?q4=eT5QetWZSgq2H4=VlG2(sJ5&?Nn8X|5L8_u)j;Yqm<>zm~u zvaBExn;ob;e$&kuJ#!oS{=@T2{vCKfYYmRtVE%|LH8CkXQNNF8>Bb|NJcToUZb+Xk zDKUpaxA0L)j(rFDC-xU+SdZ~1#@oE@5MVY`OileOTqkuc!0hNxm1y zN%`<6U#4k5rA@CP=b8L3@#u=-xUHlNcE=-3*B@{4X|z1oPJDat`(Mvf zc{kjjeZ@+7uB^w?uzrjOX@uRx$<8A*e6*2N_nn%_%7v{ zQ8kwL?QoM1F*DoC-~M>6OF-{kmIz3CyzW;7i1~NK8wt-vWrstRw{(t*OX^3$bFCjC z&$SH?mE#ICRxjo@^!*P9&s99@ZJ*OJ%hTo)g$k@@(-TjSRq7E@)A~<|imgvg-2)_|Fdalz3z=l9$qwXVfT|w}9 zS0(aq-_=sS>&RS}UBA| z`7bNi5vJ?#GF?aJx{l0su`4_>*AZd(b<+N&$Xhg;XRsBXcC*L7{1DD_t)U!;z}kh%eb)lMfP^cwO!KTIi) zjo?RnDnGFM=-PpaLG9bBF;<2@^any0A$16H4VdUYM4aD1pP<=)1^Y<#4-Q_No~$`f z9gSsQXXeh5rC?l(u4}+D>Yw4-u(D&qaAGdUXjr;6&P$OE5yB{Azs^ zs`XyTG07b5-2`&&SMyvz)*tTb*3|`6V1o&M7`J$ChuDLF(uVL!@OgI7-yaZ=DWArO zpMp2E0dlh#v;kg@`TR%^`g;PYOdB8$`a6TM^XKBf{DTbHhc-Z$D!qfZ%#M8w(X%+{ z?{AoRDsLFg2y$1@HGPe^(n0DG+*A+xTTjZfW+S8`o#Lds9`tuFqm+v_z|X-Y~+1TcO|aK zQ889>&>webB?tX+A6jzI-)|UphD$dk$Gv+c2mO5qw~>SXZeUn^(BD$tAvx%8Dl?O4 z1H6M&htURzC57&wKdISR8(<9y$Jzkb@usmhK<4GZ4*DzLnew2&6f+cS1N;E*vB4EO z4P8rb=ac7pYVV-GzeAh@IOy+PBpGW1oIqMd+5nS-{@%cwecauAa_)DG8EXT4H(jwd zz>hL!tPOB1?+|MP9K!+?X#*VDL4Ws=N~{g=x4c7&A2j1x6(4aHoz*B;I*X<@c*F1a?sy>EMB4wkSeRIWG)(FRz>$cZ+< z8yGp!26zcW6K#N(@tGvr0RMxL6K#OMU=b2+fPclvi8esKnf^h4+@b3q^miYLdD;NK z#&GYTzdXYSYXh9jyLj3F*;&g$f6@UY+5jgoUZM?fIi35_2FSHeLml+TeH7k7e;;C| z_M;7O7Ybq80C~{gOjdrP4e)zRo@fK4W-0HWKhDs22mMtuZK4hEO{~vE8{pH7oM;0i zE9D*ZCyHznZGd%5n`i^1usY=(^mhl{-a&u!m@iKoU^{c-X#+fw;hr|Y$C=*K26!`X z=V=4{9@Fnn8{mVewvlQBU%(hOTpb*L4HMVU z{~AfmggabfqdhgF6>Gb@6Rs#|;QwJ*!ZwPN^hP&}riO?| zsM^~a=gyxuzYZ#@)L8?srW$WKoeg+Iwi|21Lkl(4d1xrH>O9z!1N@K@`#s&Ue(^TX zZ{T>vxvOxJ--5a4pPt%sb8Tvz(}jl)GL+*N{WW=<$u+}=3;lETl{m)F{OLzd)AZ8K zg&kRg=|I!?IhqM%xmp6~aA0GL-4bmI{#^nzK{aeaW(tMg=CfAt0 zZqN(?6=V&4(9~7vZ4D9)PzmuuVRIAoR<$>__O{)Cc&L_!_P(}`mV&5%DqP>t-Y?1N zO!RSCUU568d#!-u-MU&^dt2axlfF6|+8u%9)USr-=AdAz#PnRW4MM!5u~&4?^;%|o z>K4bk#h~D#uWM7Gy`dMBTRK{LHnem$Z4C-g^(KlhoSLe^2f}r>bu~hnTdW|gNUI%` zWMHFd3)OWY3bmKDbq1Is%$phr=vRf zgg^*hqUTSljIW6mSh@;CmJXKGB%emEwY`CFg;N6Q?i}|zRAE!-m4hIU?6F#X@w|q< zzMi(m{yw~-BR50IK>SaFeJD!To!pMe$~zj-`QBbP9EP9%fxy5|3kXeihAFymR2;c?UaD4$gJ399(eBWrbz7B=4SgTwnA%9P3U{y`Bl> znQV)FSksAlHTGC>3`%n~F7C0))1ij#V~#n1bT7cgxU+FF{@Fk?3{N;$e^Zt*j5s#0 zYqRV*r^N|V;_pHc6SW+qra(YbPefa|5jYh$*aoCd9lrC!>Iy^U+FmY}gL}E^6wgv* z|3iQF2gG%X8x?mbzC-c7iaQlQrT9g~Zz+CXaj)V}6|;DxypQM#1I|>p=n8{dbcF#$ zR~S%qg#krZ7*KSD0e5OT(G>>wm(?x0!rD-0;Q!hoVH3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q!hoVH3@Ez7 zfTAl5D7wOcqALt2y25~>D-0;Q!hoVH3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q!hoVH z3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q!hoVH3@Ez7fTAl5D7wOcqALt2y25~>D-0;Q z!hoVH3@Ez7fTAl5D7wOcqALt2y260m!^-C*y25~>D-0;Q!hoVH3@Ez7fTAl5D7wOc zqALuT&N@1xD-7;s>K0vLaEq=mpy&z%{z}97(c(J)wBiCH!e6iMa}^8fPab2aeF3NP z{lka2mI!*%f8fuMeHZ<$e=_A-!^ut;oKZJo-CAFUj1HzqcKZL}>@~~!BcGCUb)BGyd%V$F<68q1snv3y@@HL}GjyT&BCG|*`5Y_S}C zDaI6uX+;vni3P`*ptRBirNJ**t*+E~FhRh_K=T${A-_E)DC~nhok_I!w+}A}7s5`% zoP_JLm@qn@USa*1za^}ghX1i>d_f*pu28@^7(Wc@iN5VB{H0EA$A1`(dkMgMRj$Ne zE~xl1Abkw`{V)tT+ZqMT*Pr5?o%fmynsBq}G&jlC(2kik)IvP7_FO8Q_Y0XtLw&;A zpGTwH3?C22F0S$X2DV#p+1m;0(EBWy%uF0PnYcR0i590Vr&zm4@qAD`rE}nG0{c7L zn!1`>lKBvs#Yo5(%_S5$3WfG2r|(4P=!!yxjLu~UwW81?Bk<9Uz=s-t`;E_rOr_)R z8}SWd?=lG&-wVd0dzQLw{JkD--^e=zkJ9n?dOs5E7LVUKvop$iT%VvtjfJs`H9LdY zvGQ#*V_~J^?;R+|Q;5WJ@EdC7SPi?SfmQg;8J>q(OUK`w@9-pNd%U|jEyV=Vh`zgy`=^DhYlKdIH?MTC~m6>)g zfBPGMzZ3Lk84)C=mDl}>05OjuLyOG;v?LF~4ieoK^fKa&Wc-c8Vn;Oou0x^nxY=;y z?=^S`CtD`G{CQqtwS3cYIL6<6mWgruK4!-rer{NgY)OeZlsZn>cMPfH#J*zjaN0aD zi7MY((Qzf?@d)E(;b-yeSgw)iIGMwE;HTMN5Em&fQ@l{|QpGD33yNgem~M;WTNU4- z_#PrY;xcXqKCJ#SZif4F8vaf7|B0d;?uBr^zvSB|Ydk3*{tWq!E8p(cYIfD+Y5o{| zdld8SOa7$!cJ`MC$G3yPi1~KDbED(ie-lpQje-geG>-7?;4R}>H}odx+k8R!Fns&3 z50-CtlYM*~Q*hzUhOv)#GA^WV!mgvM8@%B9AbEJ#OEh`XND3j3@a;iagt^Iiy<0o_ z8XAERybEqL+nR$S%zA`xPsSBT_;!11lJN$8 zj_~cM1p9XReEZ;!*%7|Ir=i>C+DF2-&p-W)1;XIxhd;|=y>c1s<0-Kmk5SRMCkJ*{ zJbCdh43&O$=`=Hi@%{w;GiboR46NqEtMOe-xXKhM1^zhK_{h4apsAB5Y&2|-oa zgz%^^l|4Q?D~nA{G($0h9Jj`1KitS5J;~=5&*OQDUrJAia3@IUxs8L)moPOkaVCbh zkXI<>Dg1I6Pccp7VLnF85=8@h^qeQ*9N2L2$hY`&J4@h(s5~}CZwY+N&Opx5alCHE z!p??=KSxC-iTS|rUbN}69CX*?vNW&;U&FPF7Bv>O3T~esWuAHXksjYSqjxpzmIl_~ zAviehH)CO)@Gv{Wo`fS$iyG#Q=2~3-eYrfF^8~yvN0WA6zW!Fnum-z8kAf*Pb}yoP zJ6#CRq-tulh1ZVd zpN(x;$~?%omy2|RU?VcYxgYZKoa^E{$Mfd+Ea=ZR6o+dYpu4XZ%RHMl#-E+x=88^R zuwcY8-uE#xs4n~F9D`hRFPZz}V9BM=2kQrRNA=kE8VrPJhsPm*F9lWa+FJ%0@AzE< zyQ}3ag!(w<9F zn`o^c_W~nLFVL}uol{VO-6^wQPv3fU;ps&P8J|h>K2?&MK@)ff`9q9zV9%sK3-3&t zM1ybOih_^w{}6FR^6rhY%Sd*7=qv1|vE1vt(RI?wU7Z*%c-N?_Kz@~m>+Y`yir!Nhj-?VcQY-2<zNHwRUFbB+QT(IO%(<>*F*YQ37zZmC{tuGXn1KJ0>?^+7 zbWdQQ=h^?jmg6nJF&4Q#J{uU0@<^4<*hij?a5IKsX-2~`e0Sk82xH>l)M0u*)OHh0ZAk-Ot^v$^Zbtn%SBN^dRB z?ecM*c^YoU7{9CFVApqzRX)CEOnu4qnK(>?3!?4ktMR*6!|^)POhY(oI538%v@#*f zI08S7q3UEyHc=jd8=9ikc>No~?XF$2$NN7)xaUkHph=I2OCOnXT!b_n4}=S^3+qc{ zn;_rFaL&QdNQ%?5oaEHxL!tZezMTW}WA3r($vz$^mSybRnI3ysZ1x4G9jC_h2S)p# zt%e6VZnmQ_Y)A824S$uHw}vjngZCU*&IRYa+M7AwBN!eZeWMgJiiYDpN#)s!XDf1= zALG?1eo~R&a&*5^af9Ml6u+zZLq$Hb_`Sh7fyC|V{wKx%R^*%;rprFYeckxHWB5Tt zbTZyUl%@VS7-ZYgefh;T%@;;rGu?CF6DN-E@jtXddB_Cav|OMR&!hqRJ%jI<48KXv z-s$RhjZ@gR6TS7|1l(Qc?CpNxXE;^AZ%#V=zBv%zH-W=Al{<3_p)V+X^^O4ukME)F zF5=qZ5?SF;X9>qJ9>Vf_iRqbdonO5F{vZB|Z=Ih5e-CuhZ9APU2L<4ppD#mjrSj~rokYj)q&^+Lllpf2PRi$}QTPpXc#?m_DscZLJAM}R z3#aN9gx@?IvWpI&!ozS44jAVipUjRgJ5iz|z=ZmEHp_Wz_#E%nv3yot> z%kgAbAAJj*1xI`f@hupC3pGQ+w~+eXLMbl@mIJ-hB(0p=aqfD9bDsb@xu2nwdn%4z z?s^>Y|KU>u8jLIdWGacs@gxIJ1&4eF1ANYGo z9uZxCEsb!;WoDc)dg0jd3um30xeK=KlvDivGsl$83^zQ+W2wVjT|SeHVawRxO$_-EcFsBV z>{I7BSTnsmjL44~2P=3Rd-f?*gGJz2dJClmVf3)hAMB$|uVVCx6n{|3hgTP$Rm+z) zVAZZ!7ac|B_tih_!>ZH&`>y`S|C8$jJMnNJIz66+q1mNqfPOTG!}9x~yKrL= z#>By?!}K`TFvj>D2M2Rbzh@xu8_5`RdNdq3mNjM_28?{f;q}+yzdyfwOdN*g+}T%o zqQ>~mglwm`2@eF;i|OUTvRx=IfHiS&>XgD`Do@my_TzZWPVW(Qvg>w^!X{<(iX`ZMtew_1Yr?(C16~c|>!(bsw?_Y4v zI2l-)Hl&yRy-NQ(U`^T#my5yAWS*!oeqVu%OVDcEk3+a&MgW>{+21|$5$>Vggk$*d zSsZ%K$Izq@ju^x3@wwZqr#e6CTBpVKI%& zbJ{Vml7h1lXS@rbROM1CLb}jdXQUNNM25KAISZ`1F})wJN`9I{7k~QWBE}aU7rnGh#@b%~~{@x8zd3DO7ixcH4W7PP6zlb0GkNp3w<4wEou|L!B?&EE0 z-kA27ivDs71GC4QU4`R4V&X79!_DT28snEe-sJtW(F6jTaM|O{S5S#9@aK3b2bT3< zy|Txf86>bA?#7t(X2QXKd*2L!@tNM9#+&yd0lqhkG3}Q<-n<>@r4fkfF&|UD?D6I+ zS?QVbU8wN@Vm4dlj5pg5t|#NoHe4(--t4dSV7=JpNFp-a>$5nL;(ld0$*EOU zw8@`21c{66nWoJmVfC$-uskKQk<=LwxWyz;9sM1lOpdS#*=^R zz{@G)Qe3IXAq@R*Qu!9e+tq!c$}%2A6Pxj13Ed%=s=pZr8f3p>KFcqK(!yT+J3ng5 z#2$|alLL?mE)U0~EXjemL#fYxQ0A`o06OxaMWdbbcx*lR47ta#qW$N2zb^ZO%|G%( zb~kVyq7#Z@#r1vAC2-YY!XM@ACN#&ux~t%0hRz@jFjyWA=YX?yptycJ&Y`$|9iBuv zs<{3o`sC#+_-l>S=r%!;C_ZocsE)>^?v4X!Nv{78|7w2=KuOBI{{{p2EbC48% zhhub0v6%OOFDivcvRgxO{a@kk4Wx$R;LlPPQCxoyPK&%z`9G!a5ywNqANB(1==4Sp z4^zxvMlY2T`A;#3O3D0rmL!Vny)h?A8iSxPeH#);@Mrf+IO%g04tVEmgZw;fi8ns~ zRXBJPhVuvo{4X${iJ=ta=OK(&9!iP)Yw?`&jtr$_{&(5-u32&EgLDWpTm^S4T})u-^zp{#q}(UcRm})FJ6b`#laM$k+>BHsv4IDJQN&5 z|A0>Yp2cA}QcHLZ`7UEdit7(%;TI0)5gkGm*ME~4w@id%zXQ<>M?;+NeWjqP)$#6Q zQgYdsI^IT&S5S4a@gl|bR4DF6 zitDQ>MT+b1V)-J)^*b1^gW`JcdtnZlf+}dm^d1Y#JQ)%S{dic;srm8?AyQml%W`&5 zT<<*-rZPSMDdzWl7;kpI{5psf*He|b7b&ivL+Rxwq&cCuz8@oYuekm(1pAmNu1ATS zzR!KI6uU>(680SaJOe zY{*z~{UZ9titA6HZ>+dpe)7bM>p3R$V#W18XW&?I{qI=FSaJPAim~GQGZ;8lT+chx zTj8w+_{Gn8DgLwYR(ibI{Nfk9)ETUVrMRBIF+59g{Q;CaE3PkPbu7j8laWl)%X3rm zXpI%u7cl!+aeW@6?Ll!ppWX%D2sgzMv{&qI#;64MYU+F@Q=(#S>d*AugW~!WgXbOe z7Yv)XdQeDGbjXFIHUt1B$WY`jePcwMU&we(`EIRm!w1#r6Ng#4N@2cuG2+ zrMUjPOwLkVe=+?n#q}%cZz-<7n)03$*WZc&Jyl%qL25{vKg#@jtGFIrb?~QH z(I)RFxY&c6*eD;8;`-qKp@MGiA?{ts#620s#haN<7YsR;WselgkEY}d;Bnu~?Uny? zd-wCG2ioBGLNmP%@8wsa(FBj~7iogWx%>{6sl(A~cwDFGzvA~Dw`^<~zCiFgWm{7&b8UyU(EBclibpkA4_#{sA3dzB0sR2#&}V1{X&S;~G6 zwMB=Dyd%Bhq3-$)>cFdv&Tmx~E=fvxY%T1nf?CyaLwP&&QODZqjrwq-wZ4-U`i^S8 zMF+f58g5i}XDJwuRL6&^vr&UsmzR_-RD%vJ;M7rWRM>a^AzLL1@I&40p&t9)OtkKx zX1{~N`AGM-sP^xqe%|Qdr{-`>1vi@Z_wKIMU%vc=R$cI-ZQLj$x3(pS)#-Ou)-Sr^ zqmCs%h(m4s@n2*7voM;@tCod(6NmzFTa&u#6z{n6L9FjQTXDIija^l*t15F*u^uUZ z*S!kNRtWHfesz{cb^|av^;Oe-SoTQSI)lmH0->7u4x1T7TKQ#X1hZ>Hhph=riDTXJ z9dy`-I{wiqMI(QxHXjm|v^O7BUX#58LnEJbm_%O{?wO!U?xiZ>b_AVve_-iFxqPez zULRzqoOt5V=~6s!Mx`f?D<4;$E`@f8hMHv!qtc}(Vy^z#*b$(vVd*7ltjbKAg{7mM z6ZjWN7u@n^We&V2&6$4MssBfmPoT<&!t#yG|4=miH)KEm&0FCRxZ~v!nO6@C|MBvj z!}tt`7fYwP_w(Rp8-}GB4a@NIZUc4|j)Q*_2d5H_Gq#K|r_Twp%O&pv$Tktcr%yKZNb9P z9G+>Os4;$5LAKLNAU#vQ0{EHqIH+YDUO$>SNbk$0l91^~d$%G$%-@mNWFr6>hUTlV z=qs%v;4mJONQM~!Xu@UBEja@YkM1TM!-wOZ(DSks$_ht-)Q3f7y;xV%M{(*rfc|m> zPt=%vjcm|%@BUxEodff;8VU*aWKO+(P1qW|wrNwCsC7bz4`Yr!`4O5DSpNHGI~v1w zw67KAXe10Oprl?0q+q)7>gIkN*1##e4xDeFMl=uquz8>MX5RfMh6g!Ou6T?hpBZ%L zJxshnu}N`_;&qC*DQ;AJSn)~4A1VG)v0d?xiV1WI<|F^$fb!!CxL?%)$R8)Z042rSMIs1{b;UxsaWF` zt;QbKi1+lsyW0CAUIKdCO`bPn=}%GSj&?u&&TrpOdppKrFKo9G>s%_Pd zJT&2r?GKK4W4k}0ZX4~l2fVS}8&QjMtbZAQ)iyVC)wWxFZyU=}a`s*AC3SbT`xR@p zrPFKMnLf)OAirFc-)Xx#O54fY^3x!5^-pV&M^Jrrdr#}aG+7^}yJ$P)wW!peruR%y7z?!Aa?u*+KmCvL$kALHUL78h(W$ZH* zNI%oIvnX>z`-2m3Enq#!4*{818SYeI~7;KB5$1k8sUa*xnakUZDBustMIP&wDwbH3;O2?asTwuKYxPq zY(oF!Ix(!j>YD_<^#1$TJ~lpd`yt$N;PyP+(B`;j&))eA&T$Rox^6xsHIp$z6;$-YMzhV|yPEpVF&eL3n zbX)~Dw4*kVVI6ga92zPINymYXPx9U548VsT-c>Lm!@pd|_NT$c<&MpU;UA21W%xgd zCovfQegH81eGtPi{3LcPhW`r?mEk{xr(H1oVXR;!ant5q3`Y)er}FzIQ>LE=OY*?nSZ4v^_=1FgZ#pjuFEzA4n0yKbn<~82%@C z-6DoR%`_v1e+2#Zg5jqI8ZTn_2l0A$!SGLKei6g}MaGL5{u#_YV)!>wiW&ZES&8l$ z{^ODU$BW^A7X=rFpWL&>@c)6L;qZgh8@8B8ZD-3E-(*Ur;lvC-fB1MY!~Y25#ti?@ z*i7$uh50H`o6k6f|$K z%>SB?ACg<(IwW^iL0yxRJNsBRI;Qf_f+SjPN{Ju*}J= z49^Qi>saoYB(1&TLHVCSD!FGd2d}uu@%bk<_w2vIv)`S_a4PCpk$X=5`>1!Wz8;1_;z{9)guwjcAM?%T4Wy>OA#IV>SJ?!Gheb})2 z=zPe~_n6{fmf4*A$FHx&(oYS@Ft`TSvMRz06PZ26^*8y5InGCIyMEH8K58(A># zCj2MqAF!h7{%9EY05+e?XCGWnt=)ez_q3f7NlBwqS{F-r&LI}@Uh^GACI@M$E2<%v)T=*v( zG&b6(S5rmEfn$?1w6$!rj#IQX8eWcm%AGkeGbZEryK_vYdDeHwmQ5Hxx-9eV_=X8% z$4o5CjDNT+vn6rd*zse=Ot@nF>={R9CWJoaM@*>6VQ-W0C*p6^W_-bIv&bkt@b2&h&rijGH|cGglhMm-#y{Z}j`z z>Hp4YoIMLpW&ZzMK6Cu3$a8E4+v?5qCJdf<1oDFQC(iUgnMgAbXT1#JtQTlqJ8h0L z=X`LD<1?RFkQqB=%;>Qfj9db_X~oDTM_WgPJx#=M))73O6(kIfcPzr%b7@K-8^ara zdO^wbg324+>%43I&j#yq*5+Q5cXeV-AKpLGvd)3{(EE4XWD$vRJ#L69UX4qkyx6&u z64>kJMGKpj3!R97OKP})5N3DNaLkt?{Y#NYJuJt!bWh{%ogsHHeVMYgUwX%BI@uF+ zknPeH%}`%=Nh@Z3#M5=+!hL{=JJ2q%5*aisS=ng|_Ut)4n417qa+dmwXvRhNQ+c%I zvi6MU^oC^(d(t2%Hz=#gbAXDe-8Idh9=Zju0EPW$E%JXP?XyifGtq!VuEuLj#?$ zp`kvUqlI(q$HV>2VO8UbC7AZL7ZZ!ZK5gzj zsdd&wqmO=taJ;__&t&@DxLOP($MsS(O$(!P|D5sT${kA$edAKTd@P676}7x6k%MH| zP`hGbmC-)0nSL1BKX*R|$$t6aeJ6%g8(y%HsU8vPn&A+W;{t}`=yH_G=KZ)49y02L zJB>+iB+l*hDvKSN^TG5O*Q5u|MdF+EHo>C=ZpN7O$N{n(#xNXzA{b*%Plkgzr{4+q zWZ>w*7~?k$4tDvfQNHJpCd-Fe^HKTE!MTZpQ|DaFl{gxM1!IgK=QP>rm6nF}VtRCE zJ=jjQu#7VSmgeSxq0Yll3d7NV1*}P%es3Y1J$J{n%SG_B^UWY%aQLiNhq; zc9Gu08ZN^?F!v!`4L(hcVH&hgDjN61!5!PrFhkGmVoW`UVO7YQl@BQ2-tD%sj zFKm9C_U`r%ux=d$%#NSmNplYS2>W?xa=_W!#+C5=u^Sp!2IuUjp~;cw3){)a=8V{T zeWjStWEtuwwT(k5Qbac)H>RicN}Z6t7d{yMgI#ROB<8 z@{@`(mk;tURc=@OqoT~^gFk-_GM>!k0}53Il;8cpR`tJJ@p{GE6gMh9r1-Srj}0~;(LnuxUS6iV8!DUPf?UV_3*D#Sr&~!zDMP+DgL|SONwtOCebe>eZ`{`rz@VN zSgp83@iIl3%ZL1)RQXqmZ!7+nVuJkw;SN%ig`$wRs{9MZw-o=Tn2X0eW}IZ&&=9;-iYsE554umg2jL33OnVqe!t#@pwg<%Lo5+Rc=(gM)7ls zcPf5e@kPZq6yH|-Z^gbC05adfilvH^6i-&1t5~nNOz|qk&nw=e_#MTk6n~<)Q!&6x zh542%o~U@4;yT54#kUpzTd@Mq0>(dG@dCwW#pQ}uDy~=jrsCs@&k)g9f1voXy8lY? zDEw|?K1+#srQV?OZA3hu?o#;yBA#1MsQeNU&$XQ@|34!7+&e0Ba6mbSi1ZIoc{%S( zlm||ZhnF75Qj~GIcB}UdU#fVEL{k#G-5>g>$&)6e#dJR>aXJS$c;uH0lww4IU6BrO z3v&nHII>`1zK;N&i%BokDD4l2Hs>-_BZ$?$<%AlabGZW#!{l|E)^aVITliGa3YR5GjIqt;Na%vaSZ03R>Nb+GUN=$xwjGr zhSfyS2@&9;bv$Y?X6RJI?o-Wfix(}c!v{`ykyNuAuIHW&TJ*y-Uhoe#Y-x0T8CUXZ zqMJ*9XJ6;|B@J5wTM9b{mSY+ALH2^FuobY=VR_v-uBn8b13M3v98|kXTx&rHV1Yd?0nd2*m~F|*k)Lc9r#>X1$!CnYS=Zf>tL^kT@QOR z>;~A|VcTHuh204I0PH5%2Voz9-3+@0_G#GXU|)dU3i}G|tFW)bZi9Uj_AS`AVRym4 z1N$!Qd$730Fi!~ehAo0k!;+ZDz)ps(fSmzb2|EwA8ny|x6?PTuYS?wK>tQ#*w!v=xMPV7J1)3cC&VE!bVK@4`Ah@`o*i{i%UVYk7)1-lFOU05eT{;-9x951I~OJOsx zlVK}hXTVm%a(rA3+XUMRy9#zS>^j)>up408U^l{Uf_(&b3+!{STVY>?-3I#>>@L`M zVI7P)60n7^MX+huQrHaaWY`MW8L*YG^I)rCn_ydESHZ4^T?e}!b^~l1>_*s4u#doQ zfqf2kE9|SV+hE^<-39wD?1Bo|6JV#oo(MY~_9WPoVNZdb0edR!OxRhlvtcpMaRCP9 z3(kP8ggp}$AEXP;hMfaD7ZyLT7Mu$^5B5CR^I`Fkx?nym2J#Cohpj?=(FDU`N5B@t zmcWjLJq-46*izV0ut&g-h8+Vt7Iqvg|KVj|`7+{vt%N}{uyp&LV(JzMT%YQ0V-$C^qRNq1MdW&E)tB#a zR9~KbsJ?^hJE*=q2T^^|P>SlyGm+mh3t&-wry8~fwidPywjQWWG{wY1$znXrLdoZy$tr#uxNf~0U98)YBHOU z$Yvz6DT!=OBAb-RW+k#|iELgXo0!OECbBw2R*A@J5m_}Nt4CxNfm1YpY~mWqn*S8d ze~RWmMf0Dc`A^aOr)d6DH2*1@{}j!CisnB>^Pi&mPtp8qH2)gSzee+~(fn&P{~FD| zM)R-H{A)D-8qL2(^RLnTYc&5F&A&$ThmL=#Z>{EEtNGVz{#~=`PXXxwVHpe z=3lG%*J}Q?nt!e4U#t1oYW}sFf1T!Er}@`u{&kvvo#tPs`PXUwb((*j=3l4z*J=KB zntz?&H%4R25mOo}p3qo(N5XP!UKS0_dp}u0$lAT;^A17HRAs;cIJH#< zIK9~)de1Cg=o{}8y;N8{Fmqk8c`7bT-*S?DC4p=qvqA=p<#7h*jx=S z9n6YLKbqmNJohQUbQO;8=>%+d;rQ-0Y2#GVtK^9q>|^1T}8j3W;g z;C)E%4xlmgqg}6P+^6>o$4f>w`F;@&xVEjv{W63bVg#TG$8wlHK<^C*cRx?mm~ceX z#`Kzsh=p)uGxmpNJqlr2S3AAg2rvR}#+dYsY|wV^o~yD;!u;$UDYG=O>AY?~FZRTl zFFhc3;=2646r)11hs36P9lZr{V4V4AAv9}fkmdo%Z!jgMkD^kJ|8amyv7gxH96{L) zJ@jpK4d{0Z+dKN#g(fMYJ3NOfvJX=}Rk2c$V_E(iyIAElL|#Ocha5qmm{3eA@_N&M zkYbVI5XH1&iDIeZ7{!cYx#DESsfrbf(-mhZ&Q`2coTE5TalT@;V!dLMVzXka;&Mek zD_Eb)6jv**QCz2Zz2bVsn-w=G-mciDc(39{#Rn8ODL$z9h~j3&Es9SoKBxGC;#S31 z6kk<*U2&V@n~HBKzOA@R@g2o?72i|j4KCMTF`<}LEL0q%Sfn^aF|AmlSgJTiF{3CN z2hr}6Ri3I?p*US}hT?3+N<~r9iTLwWp08M~$O)RPN0VZ+VyohE#Z`)zDXvyrqqt7- zdd2mMH!E&Xyj`(P@m|G^iVrAmQhZSH5yj1lTNIyGd`|HN#jT33D88!ry5cs)Hx=Jf zd|Pps;ya4(D!!-4$EaL?#e|}8P-xFWl?N#nDGpIgE0!phDvnXiD3&WuR-CF>p(xxI z@|&UZY{g2&Ig0ZX=POn#)+;tCHY>I&E>~Qoc$wmA#WjlS6t7oYuXwZK2F2SI+Z69r z+^G0~;wHrh6(3RDthhz-X~pLhUr^kt_=@7IimxkfQ+!kLEycGLcPYN3_^#r6ihLKz z^;b+NCKU@62PqaQ4pB@imME4gj#11gmMczHoT^x%I9+juqHy2n50xr+;J)E5+&8dV z{p%H*6tlVSp`IPE(*vmRwQdnZ2d`)vOS%7YUk$6 z)7yC-I0JXGei_{-_x;M89S1=U(l@u4!woZqwvVN%Ey(VcX*--PYf!#v?c7%vIy-g* znKkV>sJGeUwh-wZhWLpP4-PU{NnUpPzlFYqLe0fqS!rrWj`AdHW`5Nx~SO_b&L>}Z|&RI$!e#F_D7@(LT zadiGMpr$=s;A~9{%2mgkEPNl&X!JFh03%L3w?o1C640fOQNb_}FCy`=zzL{ec416zJDi{(J!%Y_WhKN)! z4CP}p++^W-DT}CJ_zX^|f z)reFu+{CLGsbG-NWTb-OQRcT7Dj1$(evt}>&$66ds9=y$=kvoEzIb-1!y;0_AYZ1D z3I>@27pq`6o|Wic1;eiq?Bk_^;Y%oys9=zRc)ZC%16#=`SqM_++F~Mg39p3lO{S;} z#@b}z2)19Wf?)#V#wr+AGjNwG7}nD_R>81<-62-NFp~MkDi}y6TAM6z$+)G0;Ryzg zH(8*@RZ9g!GXr<6f`O!mt%6}CD`BZ%c!&O$3Wi@$?yQ30d{)O&!N3*cvdMxx0Advk zzhU8G6%11tttToN{>p%{3WkeWsaOR=5evNs6%2o5u9gah=?ovMVBp*|O9jK3OgvV> z@Fn`jDi}V?wBk(`zDwU&1;cCfja4wnRvKNZVE7ldWvqgsnMqa0n=G8fa#$)DM$+F> z!BEM(EENn_F*!>GgKQdPsbJu5e_I8^X_WV*f?)vy^hO1P{OgESFl=D?_GXg>-m69h z!{=D(4@(8Z!>AxsFswyG=S~=huc<>$z_7>tG-P*_I~=|=55hu+!S!@KOmrAj4sC43 zxdc9k>IIHqSohpva2?GJaNN1j9zgvIhpc6S3(ykoY)O!bl{17UI75{pV7}Z8No201 zJwwu-;~erR<5RbV`&Eh0xEzbQ7Ys#|`y|TQ66HRLa+Y&w4swe(h>$xj+?sdaY!E^9 z|f96N5|k@qzDhc^0`%seu)_9~o?^B=on<{5teOux@ey7~jh z`TY`8%a0geHhMIc@n^ z`~90=m_@|hY<>}4mXGTy`MtY|w@Jr%pS5h~tNLWX$R5aIw>?Oo#PidaR#GuNLPf4o;n| zm`hO46EzG+!?C}e-n?`;--YSn|6!EgGMwA#m1C{~{-a06__e^rPH$a0E+6MenDVW~ zxru{QXED-C^F)pD20HJEc9!VNP5kZI9?Gl=>W<@*utQJ$zV;fQwEd}T2D790^==IF4fD6-Vm z^ihaTJ^DSbr71Y{tk8 z&$(;HNLiw>2^Lz*bRjcZ#ztl}%~0)wq4MH}#a!7OFOH5!(HLDXTC!-l)7aR&Vp&r( zl<2V6M+q|IULVtmn8Q_19Ef!ZsKXF+q*sY34Cj;#0jDxb_i6P6J9-yoz2DYoetS5#nb8vvN zo){?WiGi}77%1zBfwG<$SjTn-%D+_LJu1t3V#uqXSx*dE<`@9~OWhMen2)R{hP$jM1`3A_l=Z|w;i!SK zo){?WiGi}77%1zBfwG<$I1R)l%P;GRfz>L@dSb}3o){?WiGi}77%1zBfwG<$DC>!V zTwlZVxW0zS^)*CUPYjgx#6Ve343zc6Kv_==l=Z|wSx*d<^~69~PYjgx#6Ve343zc6 zKv_==l=Z|wSx*d<^~69~PYjgx#6Ve343zc6Kv_==l=Z|wSx*eS3iAS4epycpyhmkO zPYn4fm1R9K!V?_nUo{POVPFHmo{&Q58=MN4B z`P^hUsDD_rj`1F8#kzl@n z3!d5mmrcIhby{V7~te|Ww&oakRVUkZBpZ8x8=35dv<#Zf*1xnk$*nl0};DD)$;2(yG9Bq0kb2y0-x3bQEg`P3H{aL2k4ZBTM$+E$Kv~0v~AH{?tc3TD?5xdQ@^p4%0 zi)%`D+hKtscKgfBW8rY=9sxLpYuQ2GU=?C^o2t5d!ESG0k`cSjF=JQk_L0o?>tW_a z`7$FlVz)(ye8g^VV91Ew&SMS{yPeAl?FGBdov>qe`}fSD3wFDdA)gP+IXiy^Lq_cO zORQhSZhw(d%x=$T(C*poeyG96i`~8wH5PXJ7WT84-M)}5VfgDH#d$Z8m`IJ|l`y`^ z)OZGtHyHRi8zW}7FJ|DF-CoMTF}sbhxW&A%%_pbsVc?kE?#I9}yL}-8$L#i8`oTy_IK5YTQ&4Ze_ysTZSte$&+vMtOb~{b~ z&g^y{CS$SN_aT_D+wuU2*=>*EVs`sjihE$UIp;?<82BxV9kbhaF<#7Wf1ZWf1G_Ds z3l_UQoTJI z1$u$^5M7H~Q^zoF%x;gQv&C+InzF@iAIfrB?6y$n7Q0IB zYqBSHn_pqQVYi=RhB3Q+BFnQk>^ARJ!*0)FAwMj3dp1hw<{ok^M(6G@Wa>_EhocTO z9PfaECe7iN2?GtygkMKAu|F=J8Z)A$h<`}+i?F9Sej`f1|zMIQO`RT-z8b)W$!mnr2ZK9d6pbvZi^3M4}jP?;dVt9`0OUh5gkHF=^S{#;EtHBcu306;=(tHi_T~Vp zXPlMb9mC6TiIghrKd4wk1A1F{hia)?ppr9smQ>X?Ul`ure zzI0LTiseu*-ea>n9@Tyqm^VZ=k9s>6weY{&AXvQ1BmRuTHp&>Nh`CJB0R3pPdDO1L zWf0cHVLXQWlI37dr)7m3h8G~;e#V$^9KTx5&I7CmD#?2AeKdPI|6EzV6GoH}iIp^xE-8q>}TAlv188tIvK#xKz*Jziovy%&&P z8g9lIzib}$6{~z4Q<(Ct#kq;YG~Vtay{q77m+y6}e0$&}z6uBQsaE6uH-sBz1fU5=hR^gpdZ)l|^@K;Q0MC^{BA~ydy;)SYJL_tv z*NXl!3U0=je2r|-cK;}NRQ}S7jVqk?^vJ<34eP_66q|j)qq5y;`0OCBiEa@YwqtQ5 zh3#sMk(KC-Un}966Ta{fkK{;^nxc`9=Q^ohrM66z4Ln2 zaUC|rj!7)PBWo2H9l<%V`rzDnt*dM{z;?pxT%PnCc+hY#;erIl-&3A(?unc0@|=dD z+zX_^agwv1QX-ch6G4vXGV~;|ee}x)&A({LLhC}vj}r6mWAHd!B>X9o z=ezySgU@?oSoq!#w| zHuR%89G3CB3pWN~OdOnwPSf*vqQ>~~+rymG>lRq{Tw{#i(Qv?{Ei$j;7M=%*|Iab( ziN@^w_Aqg9DteSx@}5 z*~;|jZqnnohbaS2WgZp34~;Q?$74M%=k_J?J=u-#c#(?@aYY{o_1UB2|Qjmd{@-G8IlC1HMcj+9OB2y^H> z_cKqP=W+ga?u>6=G0GjkX#7~qnj}HJYqs&FC1MvJGipr;_HBNX#4mnqYJ+WTIzFm< z_+5dQkbGAF`Fx-bwh2Tu&ryoUs{5IW?BDd~b)ycR?#3thVUGV`W^|J7cHe?Y6DEJ4 zc}&!|F@*Xywm{zob!=cRQxfx-IERUIm#A}t;};a)=}p+q?aE{X7aEduf4*)Pn=^WhBUafl?!Uq3_SZ0n= zFd~n?-GcN9IOctUKEwI{$w^#=!?mz|c!Ebj7%RApStk1OnU)BO^Zx`4qB%;pQ5rD^ zN6^cT|1dHQLJb_8UmVzTl>ATw$IT29&Qapj)FOXW{&=8o&r$Mq9#WA11A3_x&QUT_ zGJlfgCv%kiF(;w!*!3K9W4574iMbG8!im%fTb^%&%rSC_KR$mE%I8NKI6eg*KhnUl zlz!11rCR0_%~3iOk49hUP_MW!pYx>rXpR#9W%|(^CC*3iqd7`%)34(kr5_-hgzKM} zPn{2bhdD}qJVz7x4dfTE zbJ2jDHj%g$hYhgOfQLE1mcFnxoX8<&5Si$pv}&32ByjPH;a@x}T#& zMK~YFIZFPEDK5qGJI+z^%`BuKRYPARCQ_q#C5#x(QS#$CN{6w9<2g!~GH}N^N`A*V zN`5>?=_?Ez&r#y!cR!w^#DC0w$2m%VJV)vC%r~B+G@fESN9hl&LOe(5HTuSLlz3%hZ=Q&E8HsxD$l>UlfNk7j`$)h!%qjV<2 z#dDO_P~5{DrN5z-3j7gn>d$Phc#hI<7%!fqw1b7(!yKhoQ3<1g!*B9ZLzr4TM`c2>N`5>?sUPEZoTKE&8aRH;xE<#x`5osd`PLkz7nrd% zM`;Puw&o}`Gc{|DQWgEJIZE#_e%2f%-lQrD}mkm!d1{U*_sPfBA zzKL-^erbg|I!>ljN&+$v2S+C0Kn*zbNml<*U*=KOO^ixCrzoC5f4UN!Gflg%fTAL(l1wmy&;zrlF)6I70KLV5irV`iq|KT!*#dLVdTlJjY zB!Zy}qy}J^n|s(|a1}je?so#;*kqc6!xT z`4A;aZ!OME9H#L{c&2%x#`s+Y*-ozs>G5AW%eSX_O#MJ0^kg2>7EFklta%blzF);T z`bxwRyo+$V`&ZBTG^S56{(%E}K97m(*Ex>~QKVgV#}RBozpsG5F{XS*HfXzd&k@)q zX8BET2vh6K39zTerYn6ArBM)9`py_jCZ99MlA}|cd>Heb*p@Ub133jWV`S(zJd(ox zG!`<$n(#+v`Nt#ha2k{uJ%)%sTvgTxD0&m?3A}OJi|z)tk%!JZo$`TUn^0y8cdM%b zM2T=3K;C=YFQ#1a7)3sp=uXa&c!6S*;u=MMd()qPd5IepA69%)@kffkRBTuLqhbOb zkMR#wEKw9)4REhjnSGt%L{|gwdX+_21LTb=Kcx7yqUdUXKUeTF{+|?Ot`ua^)c_P- z4Z!2PFx)AMa}`Bb1N=o-15k7|07X{=P;@l_MOOn*7GD9AxK)_1=xPAU$}phlY5{$vgm36imnEr=xP9pt_Gm!Y5P;@l_MOOn*bTt4)R|8OVH2_6d15k7|07X{=P;@l_ zMOOn*bTt4)R|7CegzYH08X$|V2B7F_0E(^#py+A<{-=iL%ZwajpeVW;;6IEqaFn`_ zRV-KcX^JD*{}CUL4B<5ST%pY87CFw0%JMwH!62V`3>QAHqA69~a+XI;Ir1n>w1Vkg z_d*3eeoa5t^BF^zKrV??E^5%&+ zM_CpdbBc6BAy>;zgX}-Aw4bo5U}X&$4*uvw@UzWV#9V6ux*hadM7y z>i1(DDv%oWBJfwY*GL!UaWD&SagOXzzAQ*?ISHrzhNCv;A!ei(K_ilL^lZ-2i#W&A znZZ{Xk$XLyNc>tT6% z@TDS^yeA)$coq4d zB0fb8cV6*{1z5!Cc+Z5XOwWIv`9+-La>o0?a4BbH{$Q5u#ff+gc*S$_AE5N|3*raP zaSS8o448&gT}Bwl+6|;QkRtm144el)M=)}Z|9+_$&@(D4pm<_!9#byL4kR-)1r;D(0s8vd|;lpNntON$`YLo*JGLEN1Y$ zgZ>Az=eA1=75gsX>f8-@9H)GA~ublrQj}kc^sA7;QP;0u@_RFH@{iaaC#oldASo(zL7H z)M}?HSbiepsH}xcwt5^8}H+32P8&rPKO?{gFjVf<;Q`b{o zsPfaUlVBxyGfJ}!c7i()0GuNW5IN^Li6d?KAm^Y9An$_ig4FXYaEnKo&B{ra(2KBG z#8pm$Cwy>8@rLJ(GyNp#HibyY_;Hbi$?XOUHcxbqCKDxeG%@ z2WU9n0QS6pev=rJ#tHbt8RQS~%MOi8GX_Ia(JdeSHlb_mkM5x+ZBBrDP$3^KF4l$k z5htfBrDJ{ay`SD6Q&L%Lo3mGE$2tKzF8q$vEXaClEFiZv6LegCZ5GzYNpV4ky|i9v z-YU^v9xkd5SH3f%EW{5rJ#<(7Bi#4lhu9D^K9mvgS&An*W=%o6==)vHM{e~L+ zBMkM%hl&NbMBl97w-k9qcZS8lcGi0#surR`_D)~CrHJ5TtU4ZRO_sM@Sk+v!4D{`O zrSX&IOh4^ZC)dOO)^W=&S-iZa7KkPiY%*s{vApM>e}&NQI70he7355577G2xDb zgXQeJje7J%*THM}%QL|wf;7x2|NYzPy@#)8(=J4|AF&qJ#KEakh!=V(Pt=%pUI5un zFF?cde!a)eDiM;x5H^6$hfZna*%U**y?)gUX8)Kc)Ct#V;s+S@AoHPbmIK@eRdYihozkLkDKQ z2P%$KJXP^5#cD;-CyVf0io$r`P<%-7X+`R3d`$5r#a}7@NwGg(B+UPK#nTnn zDt=yZm*TsMhL20&#Y9e_KN0B+RV-%zhCAPRJzE>NY*8y#&o6Hb*9Uf|yI|6!@(f5e znC^MD+gS*_Ci$%48=E@LE1~0jzV5|c53cP?KWf|QRJ85%urIe$9q!G&dCQBkKew}G z+s=MiKj>84&|V0efE@(;82q`G(8=7S0UYG`^uW7Hx+QLes7aciLpE}cLKFovr zuQP2)6KVG>-K!;Cqi=n;JRr1=ABUGn$2Ex5v4HzCUZR+*OBBI-fr5(YGXzQ~eTL1k zG2(DRtnsApaOc#4W$@0AcsB@8eB>8|8);9*pr#-F{7~&Vd<)>K+REwM-za)0)L=P` ziMu}K9F9&iAce@J=J5Q;L1ySShY6%&K99*9Xk~)?b}x89cc9UWzB?vRHwC9|bxYNH zwr=%KD-?II&r9D5(CZy%i<~>o`EYd4A4K>EdFsQ=z9cR@XM3bVK6#OKS);5OQl5eBF0rIndIL?BMEgFj5$lvAa%%@ zGJQw^yoM905qcMQuDuJkBp9Fk21_=<)Z^RCXQIjV4f>UbQX=^S)Fe1El#)qu0l`tB zRG6%!G%1t@C9h*DlS8Q}=~J2#N<)&QU4o-SDeV*n}i#jm`pRj>2yK^peI8* zi4v*9?)M4`1C}8lAY&Twbqq|=1il#OgBYWKKn`yJTfqG#@(9zFGfnqO z`m;^qd=ZP4?87=n&kgj*S8W;JQmo5Dln4)k7gKkz>XW>%woZyA45oVPq`EX2i|-~{!}q)wtZ&0}>*9g-^!rmOGd)Liir`c)d<0dbCNkfd8hE;pJi#pWosoKqfoFS9N!;0~#~Jtx4O}T?Ur_1s znvgo2$iPdzD_frHH>X7tFaJhF1+Q%n# z_*;g*!rLzXLLIWTa|f|HzRO4Tfmb4I2^^CFmMhAmHQ~|?cW3HNCf7$UA)ePMF6a&V zx?$^+I%GcmgJW$o;2c$N@1&k(^mDbvf>d87J`WT$^Gc++C3tWi7T!}V5ryl) z`P$kuQm@eW6JDVNu1x)c^_%bAC?%OED*1y8yr)nNuQ;R*g9R$KrrOw+RVuC$h5bRb zcMy`|b2s%rOsmE_Tx3v(zh^OPC1=05%}p(!f1N}Z>hLBeSFbXt!_(;Bpfae#+4OHz z8Pwq?C@)kQ)L|c1f;Xcy+hE6QMu0?K9~K~T&U5-qvSm<*RgiZDpbp#Ea4jBXUZh)@ zf2-Fb;wq;PPx#=HP}3zds_AMHX{9n5W)j{w*(TzK;d^ze^?i?^s0 z$3qrjIOJ06y;~~%6>bh&1e~Jm9bO^<&KcUc7`oL<CzRUg9&RDqSDNQn;1F z8Xflt`RKq^>L2jtJmlvLHWCfu9w!+v@hy_UP|@T*g%ty5DY;FN+@RK-xlNJW&XU}w z*ts1a=T>2IJCy$ffN1Q5b;aQ$QT6C?2ekYWvz2jG{g#i1MYXu7yY3A;ZWmT>A`;uS%0K{mAZUqvwqlWxYcYiil+Iz|#BpB4J zS1nw!q7EHdJ68`F*I3*C|3#iPB;@}AY-`*pMBRMWZrywL@Rz4DmUx?|7^b>Zg3|As9aHPr@5sG<7(E!8JjE2SFF5+;z3dc`269=cFlks}X!JJOZ3bz_BP`*8l zG2yZ~+|8&*1uDsUVDuK%19Qzf3`mWplVCNY9iHRf@{D7Uw1o)3^@lrFo*p z_+16ruJ0J6R|q$j4^g7@vN_zZ!SmOqk_gB2Xzx}8h`AO&VJ8~_X!89k&e5k@jn|V1 zhpZ!G!hy1l`V>94AzV*5+$1_Pml328hiR~=tQYHQ`Z7+P`RFev^F)ow*T@EK_m6_Z zW!uNbl{z#<4x#>fjx|GLmyY#g&x(zorCHLKD{K3+Wnx3!khbAI;Oq}H%#$21pEJfV z_821oDf5~$>>OCpE!pAa02&y3cgIc`0XHgbg8{I;q%(VH%zJDI);qB+JoJS&m3gms z%NwIng*OI{V0hpt#bXtjKHbkyJV)`9iYpZH6)E91DBh>|Z;DSV{z&n4MgG2F`v0NG zUzC(nxP(OEyMW_WUZ~ij_-V!K6u+o=pW?qMKBf4w;;$9|L-D^A`{23Ce2WwhSDd9d zN3mA1Rq=8~{?1~2{#_$JqxhQQ?-d8*rlbGiiYF?bsmR~_^k1y_DaFq!@;4U!f28;a z#lI`co|y2L{Z4`W|HbgL6yH$%jpBbQLVdS{179RKLUFueh2j~C3ltYCUZ!}R;%$oe zD?Xz5tm4lU+ZF#)F$W!(RrSA=_XFbdIp={<#Op)kjLPLIquCt% z9FVaEkZ=;gb&~SymLrP@8_LKdVd7GGqvTpJ9%S# z0|)TVKZS#r#XD2;pz_Xx7%Inm0=EQsXU_D==AEepKzZlyB5+r{bC{i1jub53`5A~Y z@B9*_5jDL1Kn3~-Pz-tJTk&X*c;|@_ly~NSbytA}y#5>=_0!6&@N+uNX&MZss zc<1>@jlA=A#*BF911V+m&c9*CG4Fga#9r{uqgc?0cV5Wm=z@2?ftg3VbHI2J?>vrW zhttcjm{A#XEB`oy9xf z%D^%2{2m)J=AFMn-w)0niyJ73RASiEzRsad@9pBcV0?_9?sS-dmXA`9;<4}h3==42L&cb>**d*Gc* zkel$%Nfs;Sov&mvG4DK%zI))Ek412scRrutW8S%lV$3_wXW}vM%y}FZ?|c&*B<7u8 zrfp3uy|*Fm4kO)hjRNN z?>q@EHt(Edh)%roRzz_34e$I()U_MlS#tXTyz@|AhlqEUndMG1-1gZnRiI+KQnLF` z$~)^f+&`3d?zFy_-=fCwpIT3O=dlQj4hv)P&S4EB+Bp>Wf_08Qc*!DNiY23FS8lUk z@@^RCj{7a|=bwAF*7qajpFbe~7u`Qqoz@9|03Q3}%UoM)diO1f9o=61AO103F8mLp zvAI=x7I(d$avnz-JKSfmbw%xxn#Ir-ZMoIp*}RZWVGv9{aK_cOEMD9IwFl!)*vlQ# z_nvdU{eKDP{9rFQXZ{2-#+;rG2mFSP%scy-b0&wKM(X^HOlA-xYvGKS0Zkcj>X36D z&l5GqFPn27(nHSqvq&#(5(Ap_uEM!pUvk*|kIwQTN>o06{W1>!!PDFi&(~RaHT0vs zTTz{ztHBFSHiG5H&n}F*TaDNE5UwYj^J@s#6V5q-fm%;E=XvNa(@Y_(@>x!D%DNfz zQE<-vOlE?vT$85A&6OPnIsOcdUAhm4b4Ig^VV>lW$=w>m{xZr4K+2qZ%dm4`4d)yU zNFvHv2f$#Qqp@!`;T(B{b2|3(&6q!e%T1nbKi?d-!+yRwY=`}PGui$GjpNfVvE0oF0g9;t;`&x~;NqfCDf=LxKDZq@b8)kr_B>!=yl ztwtE6S%7dkOc!yg5f<@(k0x_=yKXtLyLHQ0u&nEubL<7nx%PtPym%2ZhDK(=av!r` zxv#xoIX@1|wZzGAUGzWwx@BvjGS@BZl58q8?snbsgPcRbi{g;@0s+Va%qehmMLDak zQGNmrs@_;AvYg;s2nO}WR3!`b#==S5i!{v6s?c)GrOJN#1cb}`7JU3Jc2*5z1yomI zc2EV!c2*@P=Y>0~J}YJEcRb(9d=grqMcQ5W`56y<(!?*TOrnR%%#d+w6p3FEcMGzFy&%sIsPNX?sr!G z9MUIcJQQUZP>+Mlyav9&1I2+9v1IR)I3IK@4&f5#0iR>g7m-I8RL(RgNyq^tkm95e z>lQd9xR{iexzANNV2SAuZ5f{trFe01`h-I*$Lm=2Sj+J+goT#lPaz1`&QeuaB!)|s zBQcpOW87HF@e#}}-dR& zR(+U(XW)2e)kzfNomIIO5~^dDa*kc`ycF+DXgOXDkfqAUGHGjP)iRda+FA8I z`dd4zZl~ONXVnC&W9_U;7FM(z%L5?ZS@lg8F4l7VTb5=IJFAXBZldM*n@m66S(Vco zpyhb8T(#WP%kWqXgQw5s-q8ZsWP-2Gc=bfe}c~O z&Z=u!gILQk`x>+y`_jDOQe|j4{)`lCUTOqWj(1i~vhm`bRd1tc?X1eAs=dBQDq5-x zEyo9ojHSx5-HD~;cpgh)?W}qole2bK<=g>iIerMveleCRpG<#iXVtls_q4NWBLehv zXH{xBCiMy}$7e}fgiDp7<+xTvXgTHyAGUIvLd!9`Bu57-7u^h5v>fwZg_h%=pb-ai z9tP4r0)9^7UL5(fkQi91ya}>9kheD;1@!D6@L6{7bK*W6&Tx52+&ANxcoT>!NkY@@0BI2iJt5T|X?_u698}?FFao ztTf+Ay;r`BMQ!1-V>CF1r?|GbISzDZPB>!1tg?n#v&NRqD)akXnVHeFaI6JhB+x<*!51CrSg#VMS7tZEct3LSR;4an$o6aljCKrHqp_Ta` zzDBsQW$EG?ES862?1q{pE3nXcam}iW>v4fa{Xo3gYhB9U_2^B|2yQ(tS;zdj1Tbj}9L$%f1L-(IbBXiw~rAt95hRb{# zx|$!<&D%g16vq$mP{!&rsx0qHoIed~ z8-}Ifdkuper@L@`ADTEgmGrK+9L(votZ6Yfn=M_6EzG+v*7<{?_0p5s;>Rd znLU9ai8u%m!~g>X2$0O2KtNQqBM*fn=m-JPViU+)L&$|h(TflgP^3nKXltuLe6_T- z_u{Kk#2Vk0VoQCCwoPW3O0C{vwXIbu|KD$)b!JW?D7L-*`rYq;zHjDt*53Q9{aSnN zeI9FV{M`Ji!7qR?^20N@1iuwHck{ak{6YwG4kxbXJKGJe@^PKwlunR`F>7x|?eCO)!8@40IyK|7vcL*TqsrZ%RXW_@|X9Asr@rgMxSZ3I-aNX-* zS_day*4nETt|+)_06L3|ZU)6*Ntw;;XUkhADlm&Qb+rsd>_DYy*DhdOlZ9~END8GWc&{Ve=Ml?hDEsE8y2Ydh6VD`EYnXG zoFS;62_U>wXnH_px>bS?2|g*PAC@3q?+pv2kqp!OpbUru1@+#rp!W%__l5<1Q0Tu1 z>b+qRuJ?uoW_oF=s18y0k_(0XrJ(0XrJpxzr6sP~2i>b+rsyCl8d8y2+Q z8y2Ydh6U=qVSzpfF!R-W!vgi*ut2>xEO3^@UnHpahDEsENDQd=h6U=qVS##YSfJh; z7O3}z1?s(Ffu9H;y*Dgqy*DgS?+pvod&2_t-mpNuH!M)^4GUbsr)WUEH!N_o(0XrJ z&<_f&_l5=ilF)i@SkPXdeZAfr7W8zX_1>_c_1>^Ry*DgS+Y|$}O);<-w?tZZk>wWfOA@Oea6nL@r^WCnsDQ+0^Vtl`=@UGQst>qIB4B_@Jzr3T>?|ZHG zn4#m>#eLSg_@D7U_Rs~nznk~#IZ^I4YZHeD93>eMVE%=RYSp zD5W(XnA952#4ii{eg4+Cx4pf~sK+m`Kc3nIG>mu5z{lV@H#W+c+y9dzM+W;Mmvmyy zlIo3I7tb8E1w6LI%|LTB#cF|h`rMe&be-nwtG_OqIjS{&w_)tbf^TldlP~ikKhui| zwWB?j-9frD#(V-eAB0~f>Kc0Hh4_&+Pt?ieac2yiXvdbH2@9Nju8Z`^sW8U`G&dkW|@vPF@gzl| z`p`3bx-0|VHK6}qwu4~Jza}tw8kU&l^3_hYs*{X3lFr$OP_9y&jRZ4!?J`i z#-PT1?(&5@`A&0hSwAn*&G`+=^k!@H&;^ej9C{?w zb>Yy~DC_6*uZ{Ml?|AV28|pV_HOdINor`g43jfhK&&IbH{y=jqJJ1@>MtK9s z+q>?;IPHZtOhFj)O-24}FAL++3{=OD7H8r*ek)=a3u1pba%7{4`u8#B$KJ)cWv!2z zR&{({ur=zjsyi2!PLE=J_0GL{?OoRnZRz^X@b<3WR!Y0?pCd<74eRx|h5A^i?OQ zXo_V+KafYj=ohQvb>Pw4YW_?=ux@z6w~PI82d<~AF|A>3j(gEIZ07*_BnSOE87{31 zs|>ov?qljIuhkOE2L0BjkcYPY2gVr}$KFSN7W7dcTnkx@VW@2M4qw;V9Js!dX`DDK zJT3askt5$Wtj?G+in~;}{r=2wYy4;6Mc(5@KAs^BKA*=Ufa@MZI@X!jHc8w4$Q5Qv z*!>6#P5PCVIiyVqSS_9d><3);eWpkKXPmlCxA_3H{{xVxW63e|OUkH;)&9Jg2W{!` zukDAv%b1Ba#a70#8__0?yzjW>g?J%k81e=A|5bcY1iIf?(A6u_ekZRtBkND%+=%fI z_h8=gfM&n0GyKtX^y{w>7eKydxS|tZqsHFAQr!=Hf`~TrTAW|D=()!ax#Qf!d92}# zZE=okJqIC;Grs-d8G0V#xQ1-+Kk3sWzvnpTm}l7AXdAy}k5TBP4CQa{hp}@ibVyeg z%0+!*z;o7nXgkBO^;Ib4g}AqA^8q}O&~x+Q>$j!!Ds5L!g4aB>2kMu~`uY8t(Nowq z7$+QqKJfFQ{qmb^eU=HDPuq-{F`(*)^u!Ye|-sV_T2qw(etDuUWCKWO?7icpi$nxW{uE>O|fLj~v24z|gSxd(A8DVDKDFD2d3j5_uXlSLW4(JX+JUm=g=_`U4gtucA-Zgwv;!WS zeXcIsKI!;$*>4Vaulrt56X(uj+no?)qM|mQYZZwyx4Bkfm$CM z^Rd2YYB!Ch9h8$S$mt5`YQ5i1xIJd~!Q5EpOzE4$M^Zh0tPOBI$20frId~Z31GER@ za|Lx1eJ^DS&8`SNTIo)4an-%EwM@!Uq44GSk{&&mu?NauQC_815WuiQ? z|1N=C1Omug`eFgDWm{c=`p>xKpDUAB_2`#M>6b4flffQ!E0?;>125K#bsDAT-?_0{ z{sz5=`ohy;4C}{}xYp|3wBe-J@?Xekh5jkuye)nv(z@F!BK*GM;uqkThASqc^bm0OO60=J*tm-!RGuot^@no&p{o z#HZ{<8&S_6B43o7b)p`>RQNLQiLSg^4|o25ByTAr^#*zMEF;H{GnaVLhFoLq$a*23 zgLa&P{&Ch5e>r?a-ydayAIBcYb7tw5lyCp-h+ZogrPZA#)?~Q;iCd*DSts6SB>1Ba zyl-b6c34k#{q>XXc{9&($TN{1!9@)%OD(H|Y5<6MfBkV9oa+)-?~~S;WJ@d*4Jk-Q~Fg*W(UaJDVq3`0?2kpL75J z@A3Zy`-qK3LuUr{hG#upx_!akVZ2CoAWLz3f(yu}``D9>PacQM4rD@(z?NhD&R@up z$sNd;I4enFf5&7}!x-&Fc4=w65*|FW!6XxpNwZUJGl7(TSPYE?p;?m{Hw5YP`u8(W z0x>?t$it|}NrTA->D+wK%rBURRP<*V>fQu_G>Br-1OYawQVYGE+yp6@CMHUeHIc8) zHBiDy$%=d>eJbDH48-F;tmJWcQ35D_DZMG) z*KrODh3|Qj779LGQLHIAwY!j{ZK1&DfZkErh)MS1kzXa5dSc{Aql{qY@ShzaW={g+eZ=goVOWx z777?xhDur}@ST|Y0to{Efi?&ppq5}y)YZAq=fp-z6@6`3k7Z5=dw_^kL+9)3b(POE(?Vc#=9&O z>PUZ{g~A#nI9>|{1q+1*l&z$N0v_BM>dRUvKvYlRSVcE!3x!8n>HjSYg%PM=$}6}z z#QcV``^J9^v4^q3ZxLelN}=bGl$qMbQay#v@Jyh2gW^XPeX@VVtIo%tuZAWLCtQr@ zw0Q4?1{;+2JW}*Ng&|y`yiIEG0VJXk<%!-Bgr9oCH}O9m>C<~>rhNl!dJka;(_e=w z_Z~VNVMw2rgF^HkW>eg5&_XKj)d=)?z1nA@*PFw#dow>kOcf%`{U($12;*m7I;0Zj z5w9`Yj0|an2Rie$zmg~(if?NT^G&8UBjC-gq)+sg;(ypjSOl7vpycL8Q?nzh0ayf@ zmj?JXin-pTeTHX~HJseno0|Ps1Jh6zP!xQ#csNZo)|;ci(6cFLgx(15>n80q%u7a; zRU76FnzecH2-s}2+ULb1mL|^Uk0=9!8|^C=j?@?4WU`&ii$;D2&1AOei#(gMv!=5W zn{?7q=NsnDIwETdi?dmuk62!Tedz9x3$jLDz*=k(GB9!wOSM(V%#mFrx0$quQJeld zbtJvcn>$QeBK4ZT*}Gt~cQiMAHqHCZF&9k7`g6>TnbQq(%&eK%a?}|4Df7MG9Kj@W zM}~Rv1HyaINPfX(KB!4^{>ZO0r-y86!N^A$_pq*pXVZWYy0;!N*(GYzZK)$_mR(_( zJ5BbE+Vpy#5!?dI{IQAcqe1WJGh*3F!`x*K(&Y9&BWb*5J}%t+BhO_yo-?N)It2xU z@Apki@Kf_F9m2cVY;9b)*ZVYdW%jR_evfwszWmC*lr^gKEW!oZ&$7sSJmoyUlg)UF zH-Mn*x0$}bmrXggx&lQ>_wucB_NmNeftQ|W#@&gcPeXR;;Mb1d_<114e~o|D{9A#) z949jO8c=3&?goI`^t>;3vwebs2A>Bb9wX{k80I4i?=c}{Q$}1#k=dzsAfabdz7gy~ zvVfE9QxB7=O&!SNHdOa%{6--Chf2#<1mlQJ$uXv_n0gyNwq?R9YK-B_cGITeV`I`7 zQKvuA7{GY_7utriKd^QFXm_slOA_yK*yM4{yvHGvrw7G*94~nsE$?x#OwgkG!Eh9N zVw7$AAxGu}-PwO4Tf@itZNy1_3;vEM4BHm8_k;2}95mF!&o0xf&WWf4*SxB(Do|GitWiIl296E7wv3f~aw>RhZYJUWWC)Sy-*QuGxx2RiT^-cZbTYiR$Wte5?9;w1;*1*oieE zbwY5?6D_`TLTc^mO6A|08(P0jrTH3Elc#bVOxaZP8sEYy)wS9eD6(du9_Oq3&8_RP zHD+FDd?-hKu(}|3PLBE&OpJ3f3Vm63U8eFp)u!*ORi4s}c2qm3D5nU`_Q;xos-oPG zXY|SB8p4YHGP6a!ZM>@bpcUh*UsVGOaznEV3v(>>(lrHYqv@N9cKxkU;9I4xUE>Q> zsirl%7~F=2eA{SN>ro`MLfovZt)4j6$_<6o?Z`y^1w1#c*@`q<)n?#Bo>$e6SM5^2 zFq?gsst-)G`hUK9?YIjnRJ*4@ZT(N*RG8>h=XhwM7wB)+S;-1xO1jWpE2`X#!;oa?E(2cpmT(A zGNZ5WMsuAfv3C-~UdHS+qi@!XzAvZF==*x=jpo-q8`bsR4Jp_4y0-UvAGa?_*bPz< zTAEMNZZH?L9z({d(3?k)yxZ1Fk3y{`wpv0``{A#B0mh;n&f_tXSy&_RW2|>kFlJZ_ zY{$Q7G!`S5&i)&Y0ZD2AfSzK3Ey$qjnJNruhgQkdB zfs}C7QBu34(l*wsp9EX3rAuI9b!8b$t-#+{Szc3xz$?odmMvW(VcPy^MMW7jT3uB@ z|M8I_oKV(Plm~2kuH)Upbj9U0^>*fE%a&bUQ(>@F%PUsYz~rlBMHTJG5*c)JO;%aE zm<6j@QBt;YF$zdCv=aLRc-o__Xsm50x!fpU*0`h=CTL|>H0V85#dh)Pisj4T`w?|r zp)0#WYNqus`vHwmTTv&W_C}^9n!&$=RpXU4bkiO$E(lxn?)_q9A&16?-!7Ew8JBE@Hd#BoD#~|JbN=htRjg0Fsh$^?Zl;{9+ZWh#{v&3bFnvTrEva2xcR2>FQ=>BIr>bSkuQE#P zja9YsL^s!Pz@vATHKH-K1|bELQc-wiY~`HP(qExvO_qd;29E~gGS&P`j(m(^7m zm5VR0FzS}+cCeKXO7$O&i>bvZnExk?HJ53X6I`w_2R zi3lTIk&_5WevH3L5TK76kgmZYfK11u5kGhN4utKsAIryerc*xNMLT7{DHf=xvp%u^ zoVaU2y7>(_Cs|)Mx5JO`fShZ{ArJflJdwkR`!;AdzfADM;|Sd!M2FuGaqe7$Q{zJL z<2MJ+;l$}DM+l_c@VFl7`mtl>U^@Pu6_jA(+rgvr)9)v-Qgzb3h;&08!o`CtSeJMh zhmh_!M@h%L9T{NsPJ9^ChJZmh=le(2m-S|wxcSYR+}$?XsV|asJKW*_N;}dpO5s7v zV%WJ3cWGFk)Wkgq>&Q=9hXZ-;(5{P-KFt`1fQAGcoB!*H1~hg2;l%BO9naeh`>^A~ zF4~D9etA3A1S8Ex8TdZIbtrAQl?+g54c=Ed2kYpTS<K!Rg|P#Y<{R8f(jIu2{MZafvl2R_jQ3<%)_1xY4MrtZiJu zWz~wBx*FU^HSpdeVY$1sVkwgG77Vv4xCg_Paznx;co}bCuo^@#5|&lF*R}5ZC8!J| z6N8a*=Yg!P6kH|PB)Cp+gP^v9Mfx_OZxQ4xDDt^OuwC#T!R>~ z!qey_;=?YV(CLDH37-laf^t)jd$R3ynkU6xzj#na>LvCopOXw((79;of>fu)j8+t-8E_Vqw* zUk}vw^+0W34}4eBYx{c8+P)q*5OT`=w0%8rpU~RA9<;Wv2mVFEdqHNHUfb6LvxL_6 z^`NH-t?lbUmkO=z>p^S#dZ4zi2WtCz;149-Ew((79(YLN|0byS>_)gBdWC#W6&x)%N$@PeLct2brGoz{c)j3O!FvVo6MRJQNg`zX zS-}+ODCXafi0}--ObMSNc#Gg&g7*tPCm0ocUl7Y&&DSsZ6~Q@ziv%wdTq*dh;46YK z?A3boZJ|FD`maLshL7b=A)+4XLiZDTkkCVf&Jud8(3a4Xgytu*{=XEAOE?`1@wyWP+o%tbPR|cm zvg`Q)bPn}0Xp0DXk&`O@yO%+*b-*5y?~J0YTzxyV_v{3uWLDEM#~#yk1|JDqs&qE3Sd8D)?XL%gkduz zJVt>>*cfQr%yirue||S*{4FiZlw-8ldA_J+ z|9zAL_Uc+LbC56BzY+ZQLJ>sJ<}62PCj1Y;e#wvVH6Q@LB(8G30oz07 z56bKV7MsRkFR+|8(Xj2I{gY$spNVqrD1EByFR)p{Sd3T0Cd$NW1a@@FTYp{uQ^0G( zncJXC+G8VDUuTyIx`n!i_G8tE)3!@!r*fRr)(&;(pNjOl{F3$?NE=2xZ2#Yxaw_-* zVAs1!_(^*`h&Yy^)+qz@5!(ayrtkQePY<4JCA|k}{(v-n@~-b}vDS5xht)Pc`i|7e zkNCIsc_;K_Yr@}!yFW_NAGF6Z;lp4wY_4c;rFOJ;joC4~c}!b-3+?&SQJ2ZrTo=o3 zZ;hg$@EriT4ZjGV1v8>^a1H&i96%qg!`zaMb%BFC--obUKIgXsar`muv3~W4KicnD z>Ay_)%UDl+QTn6h&9E=J?s(#vKik-7>r`gP!07v!4=BSI^Fd%I+WE&E$G?WG!EcJp zN9pzOBZPFeez?)r4{aCg@tl~OU=NC6no9k0bB}SI-bvpB{j@)h-lf-ePS!GFmw|b2 z=XKB_)E^6mwMJE7ZKto_Ts>wncf|@nmAMc)MC+!{$vaKw{aHTn1v1z$E{efUF_mS= zkTRHU^nWri+JG|n>jy?LZc~)M-Sqm`dW`nXDVcuUX|&Tn%EhqbZH==$XI-vEVwLZS!E2u}ub)6>s8E8G~xBVSh=M=>;)}k4GZGDpQ^VTSB zv{RvDV22MMBG6G7^VDCoC;SC@W1P-L`};(X8UBqaKI#^E~0(oAovb|9`<1ElJW}d2R-QnW4N79Wxgi-m%v|B7sPBZ{akofJr^5* zahL+#VTSh{(0va(Y4Y)+eyHy=u*ZMWu+~4b4sC9tPX-__6Fk2QJ!pn6i7$j6#~P#m zV{F^N!07iRuk;So*ILq<4jXob@;`vGxOK!n=m_=|@h0?>)}^dF{ieV!8+{TtN}r0~ zgXRO^cVA5WYXWtt?}L+EuEBMhfnYD zy3RgrRh=K`G8D&_QT9;A>uGP4T8xVwBI3U&<}* zg0VKm{DQVJZjEO@40-L`hc>(&bM3F+&T4Nx$p~DG@zmb`&f!mDUWD%~$Pd=MsXo~1 z8`d3KwnMBx+KJry@btm`_RwSfV-wz{V4`S{Do^(i9P_;xe}w+5-~Gqwkwuq688 z6UzM2<-E(0bIj#>Eu+^Fs2}UrvtE0SF$N54V@iLQ3_*8j8TudUhQOXU$0hvCLVqvbWt?(^$8@&d#hUC`as-v0kfSLHIqJTy z;ynUoCeCz);6L$%F|rGtNB9zcI`F6fK=)3rRUf9C2>yRXBq zKweiOU#)WkHJyE+cO4zl2RfHJ)9EVaab_<=u%K z97}%eHTVPYP3|@LAgK|>$dYDq3N|f<>@|1~`Z=-JAbQY{y#~+JWzm-W&{le{!S`7o zwQl0V9PCfc06X&!P; zSn|Kg!cTH?or5YS_Zs{)xOq;q;|8R0*R|wcgKsnC>2_RZ+N0#s-I5;<9gkzL!9Kh& zX~};nF8ZRq25Hl&_ZrkKo3P}!SiFnu0(sMZK}HEnewM}ar7Zd1M<(2B@Gu)PVafkf z^66>G{|qxuTJmGX^kw!MTtJoyOa9B)9NjGWlY0$*gGKq_a4jE$(#G?;geAXrf03}{ ze~tZ?u;hP*JQJ4uIgIOO$$xx%4gQ(=CM@}XNS@s+`ICDM>NkErwaZzQrr(q9v8hPf zT;`sz2y-7WcDdks$EwMk3ld(xl{%^3Ela~A|m~YaOA2*!t zy$1FB-=rn~L|&V;CI3Q}CTYq4EHC>aOMVY5`G3#mN?P*&H`ydD`3JJlpJT~? z8Iy}8KQ^(;xRmLWmi&jwJZZ^)JyRzw`JZLtq$NM@OrWPc!`W^lXJz04f#KR~a1O6c zTJr0iEW2CsyY?FVF?l2{`8Sf4%aZ>Z@^V@7XEEMo$^R%z;=Tai1v0Y`G1X;fhGTg zsDNY1&uve8*_Qkd{cq)ti z17BotFR_9TXdC-proLc)=6ci>aeO^w&K*L#-dxTT@rPIb|+t>oQLsG;?i0UuG?dbxPm9F)%NucJJh1`T@+ zEnyC@Q!W^GFR2WP91yscFR(4_6KXHym^WIf1ypLM@v*fOR&#V%B2*qLZZ zbFgV!QSb8%^RUR7ribsXH~ELb@04Kn)>znrtH8;j2@;-JCvCW zw+lXR=n4=c%xqkSy&P*mj5LFq7+zTmD$C3VHN3VQ=cCMXc+S(&=J_}s$`oVFuj=$e zYe|hY`2yaYJM3&0KG$3YDg~!lsq+gR^9An?p5rMN8rpmsbr?~@@EUV3!`HErKlScL z(1`Cde6LPFa@pmExj`L9_l)3&HD;?m&$C6TRn=7zx4$g5NlJuR#-$m}n=)zwb(f^Vg>Zl;0RYZUd?iF!mStGt((4 zN3^_g0Ik%6Xj}zDKc-2%Sv;Ob@gLhGXuKXJAL{|MU_1xV$D>dw^CDxcAMIhLvx`2^ z+{kIW=B63^!DcW{55YgU82mvqm}iW=kPI#(gWqWeyqGsd=B1iJTr)svmcm^`Tr-%j zOAyx#78p4NxHhpTXfAKk`;5`)%-jNWda7RFo2EhEVID(&!}fo5x6otkJNgBC)rr|X z4Qp5E&q=hMyki^>kw_TF<6I2lah5QXha_sdI@rLYiE*0Pb<{D9M{rrgRrM8#6ty)= z>~m-Wu!4Tt=rL7lP+bw^|B#@%IH>v+sAa*B8WWt5PeXTWKdiOyPz}2@rEZ3e`^PH_ ztitgsEm-9F*gU1E9oE*rH7X~ly+&{;tk-{3WsT3Zawn+ttuSeSbwVgtofuU0LG=eC zs2)mrRsHO$trJ2yAvF|P7Qp7a5mxK4Q7{KwRrMuB>I zbwOcKs9l{JR7-*r)Zn1139c&)c1yvSs3|kQK5nn^UN6^EB@cb#I;sS^rRUV&9LPT-e*^ zsJqt`RKsh+I<<98hu_HUv#KR|6>0se3eWbQ?r>_EUi z1H^yh1@Uia{QqVVbFi3GJjXf>L1r1y3iiyh+6e zdFjRp_>AmCsBkR&@pIebXLIDpK`jou`&oUP;Ar9~Huv}jOCL-Z!P$(y17>kA-|IZr zs`cJ=DSYb1gZkS7&gh%8$#<@$YF?z{1|%;Nw;qA#m{BFbVA^iM! zgF6e@4#rR8$4Pq?eupse79n$f08olzI!6c2y?C6*YpU^c4yK=i^iDo*{`?rBCx7R< zsfc5|bDb0ZkN6b$j0b_b#Vc&{WTUpGpP5%>?&Dwamn=b*Z zduqX|yGMa9?@ORWEBq2|bABwp0*O9{FjN{P%j!xhR@T5}!g4wZ5Z3~T(&oUf8t5|D zEUj-`USab;id%ssYumCn=~keouD-FMq;hdhEv`p4-JJ{^r*{EZ{alTUE9+2D+m0I3 z9^H49OslEV<*Qn}bm?LphcnnE+vYz;Sy?4*?Hdr}6cGcYdv~C*tCyf;waZr61*Bz> z?z`^kmgi5wzS!EMK%KTnclj9j``rqBjz0m{m_?zr1^dyR3VeAF0*h;FOSDn8+m%2M z-vPAAudG>VZz~LW`5f;7i>nsHb{r;FY`UJV1nQPt)j&rQpJ)1yJ(J6-6H=0BciT@! zva#Vhp|ZiKgTIgRqk02Ci`k9<9CLpy#xztH4*-qWX1K0O|8dM6k2iqiR?dIla!;?- zLdmt>ac&6B*6!v%;PbbFcDoLszmQ{W16`{X#Nu+=;LR^WQ^5Ma0xQUd3bMy=scl%y zu^@XgC*232u^7t28`>YqaeEZ7-BSGXdM40-PsX|iALgBmAlxkSF6F2`0;b5jIOj^Hf!98I z_JJQ(mAZUf4Lba`;@r6gr^aL8x05GwIC1xZ<~ikoN8=bQJ<)H@!8raN5p=>Uav?Jg z;fm|Pxx$LliQ0m6LmdL>fM{G_+1F96=_bmC_pwyi!NJ32JuS0*ZM z$J4&S9nbeU4o&{N9dT%0e<*PqVPDT%0eAd8E}GZ#-pUo}V40P0U3yjP@c{aBmS|12gSGdc(9GaLd)`kWqgQ4e~451RLs z&cW-KI0TUN(jN3xJ?M2k=o@>`xA&mG3);C4X8j)Q5&tM?=RTP6&-I9Z3AA%B%y`#> zGg+g;zd+Vv@hsq4=VR5k97|ZX9nE?k_ zbVvmM0u9TsMy3k_*PWFPZq&hc3Z1~UG_o&*i$6wmFNyYBfO0sY-F6vI1m1$W(nh-z z$kz)81P=T@Suv)NIuwJl1aHZfX!6w0Vf*S;H5Ns2?MesJkI|SPW?-ATC zc%R@7!G{EQ3hox{5ahhZcGcfH0GotfC%6O1@$SVUfIb<*nCD3A?-!8%HqgBO4#9Ro zeig>>?Sl6S?ht%PaHrsI!4AQv1)mq(E4WYaWx>}4-xBN;JRo>b@LfT!yI8Id1wRrz zEXaj40+39b{|Ab5jdo8T>iw+Y@M*e-aF;C8|L z1a}BNB)C&>w_u0h(}K?n7UPB_Gk`@eaX4%{u^6Qs-X_>7n1kgI)6F6xT`h40)~Q6K zpMl4>^Z{~!2%5_!Vh%COek@G7O~M!Au`PXrOu;gW;Twp&UXbfL&uGXKaSVJZ5VPUi zg2;su7A$z6Z}@?k<4_*p5G<eTx9KZxNvOEdtcOMS$A32vGYL0czhOKBOQt$m9CwQmui_ALU`zD0oAw+K-C76EGC zBEWZrkM=DBTKg6OYTqJ2?OOz>eTx9KZxNvOEdtcOMS$A32(SfXjpf$9MSyn;t$mAt zeoAQVTLg4WXzg1BbUwyDuh+gsfZDeRQ2Q1E#wA|+76Gk&ivYE65uo-h0@S`mfZDeR zFc12Ue9sY_Lxdi?ky0d)-fY1s7)h3%lWUxMA8q2t%Zeb%}-?R@B8Wa+Qx zMAKofWA1>h5O^$ty;iAV+X9zrdpg(-!JeOXd$cv33R|mLi08iPw8O*<8>9W0h3mLy zEYxXgX=F=$inigJ7kd-*?#P?41luP%l?TncQ1$+sBw zAiwn9c$gJ#k1Y&?j&#PDPXy;v@GFY6#$P-4h4_&+GitVPj0WIaU?J*4KLNGyfzk;< zp%00K9|<{k!W`s!nQKJS)ZIC9J^p#Ho!$pn9{T^Z)&PqPar{b9tV z8IEmU1ajEfAJ54KQ`Q{0s(~zgYP|^=nHT#>`|BM4pLdqVKM~sWZ0pYqT$( z74=2dM|Zcc@5+Q-VrJ>>@vQpW<2yTBqlH^xPv3<2&hw+*KzrBDj`dw;A!HshS_+%J zMUa_3uw#3AMr(X0etXMX<1aTrW)HN+pMM%Qpg`Mp5akPFd>Y2CKJGGyJNqw%O}@`) zite=9y298m8vE_`!~VbhvHvgj(`xT}1U@ayH({f9fjyQl$2eiVIYvJH)ZW{-7i@^D zR@?hy7hzndNFD*4FT@!3``5-6`di}*VV4+yFCcu;pSmavTW)I|Z2zA^T{a)|VP9yD zeKTN>_17l95HCU+cwvey?AUTIWxlCn@7)VKTJN0#p1rW&HFN^}7|>4v{G6c7RX1Y% zqOHB~^D_<)hE3Ra<5_F!Rm#{yTW>7yQ{TECepH+`C~fb;*n5Y28?(&RF{#L(ZD!HF zGf*8rTAYdNj0ng6g4iF99NB21-}@NzW3XA@Xj$uF7g`-(igscf;k>iFbb1u}=AC=< z+PkhD+S2u%;k12BiF&|?wvra=-YbCpyKx=t4hJtpznbU^+AkXx{EeVa++Q4fnGeSA zsBQWu)c06z%5vh9(OqoY@9}!bj^AjCEi}+4Xv+ZnRP5*V=%3#3iQx1J^{&$|-@((DKtiuk2uL#In zYkWJxUYqTjKeC z)HS@glVhtt{8;#bpTLHmJ}|r%<|fQnoVWbgGo16*j>+5N9QVEHF9Gq+`1glrMD_fL zIgE1|$&7k#h8(FXJ_ z;^m&>7NM)I+vtb90G1Aoe4rIFs{bm{wgh8JnKiIWn1-c-%5@jME(|@h@ zx4`^zjMlit`bqn+T;y3DXpMTYwqg1l@T2|n$_UDWIx@Z&_+uaA!8uJRH{_w0_59oL zU-S;g>gSD9u1kIe8*=s!$4FCTeQY6kzRED<#jvpGoL?f0x?R^T(yp+UNr4_nJrC_# zWnA1J^VHy?4z}B(nAw4N^cD3K{pmnQWni48Ku?{7vF4$!QfRk9m_x#-#~@?TRx811Z(_Jhz((R9Q+ak7NaGyD;Wy zYG13G+BYeTzoBdg)T0*R9KU|dIUKtf-ziv|r33=a3i>YPTc{u9Nb4EMVQv3y@Egz) z^*{#DZ!r(_jt4TbvJHMj{Lr5k>WB6V%NU4E7rlb8-@zY&o%JY4m%6+dx6Fri{|$JeZZRX$h_K&AIR{YQ#~|DrPL6e4)A{@~_}IdD zHxSOT&av&RQK5TY!7^%pxB36D%>R%>j(O@Qrl(%I7yXI3S?iPCC&Lefe{CoA%F-a$ zJT1}Q$fq~j(FnNK!dP#AAF|zqx%2^CuXSCxQtPP@^%L~eSs zddt1mW;v<9w9djh{8%~*bL{@TsE1o;y@)m^Z_KrO*hX%?Y$u1OyM3@s?AA7qNZULP zUe4M`&+F;^WA{V8y`|mx_5xqt7dY+C`vSHl7BPD`ro7O3XoJ)U#zj8HQ+l{mx63$b z7re_)v_nt*eaJd~{e69pwzx^!0($(YZE;I@n=UhD#+eH+mbbt5noQInA{rw31q$j{a+6qs*Jf^9^AKZ4B*R(*qP`vx89;Iq0XZ0%|gHGvDv|~Wy=fR zckUq{Gdw-Y_0D){cNop@-|$b?BfCE7u17llc6@pSz7(KA zHNR@waMB9QOYcC&U%zl$d_VNITjsVPPtNxf&@S{1=8pS@#BD`693Av~T(8>$?e0Mv znAYnDf|wIH20R!G{YHJcG0+8l?aZks1-s9w>oAV%ImWGXqIzA4zTXWQK!0`Oehu@$ zG>j|sKlENgm!kjg0?(;o$O&``(^FEwuKsk9I`Bk(@J^BFW zUC=|jyhe9DZ8-YhU1rGIeJ+{xgv&Z9Yp02B{x9&%jBJZzU59>79>q9ah~FaoO7Wv^ z`e)5)1AEBgkmHoaL_eTTJ?Au!$Wy9Ip13xlU(WgPb#%1({@uVZ@WK5##wq7|JZrP% zPsZaq8ILcbt$Y{$bM@e>A9o**Z$U1OHLn|3BRF%ryA9wMtecL10z5h1ehOaBn3{+2 z`!ve_UC5tve{u!%9@l_+ErvB9*J954@0m}K4sm|m`{3IqJ6)LVgLIhBoOHbZew1k= z_B~I5lUBE7lIJtaAlr~(_t^iYlyjS_obXks*QH)V@-mWn&Byh;Pc`v;=C9E4iN3_x zQy8zD52;&qKSK5&1}`thpnGkEID2g*W5dzGZ(%$q_??_w3;IT)zfqS4w1qRjLf>^J z+8^t|r*UmP*797ddje&hUdWJpex~lg9F2T0OzHw$w?pnb!qBDYhZMAJ3g#UT*3BvR zLsznI4sosQZ;7(~7GWL!5Zk(gZJimrP1?HH4_$zMVVm(ehv9FIulX41pd*gPd%f`1 z>u~Va>u~U%CH)x29JWz-yVvUMYs$!1kj~wYqof~mUHy0(N*U5! zWf+SxxYx!{BR|f=&KTl&DncJP>jEdwrKtPitYPuP`6wUmvF=4#kG4J-lrij>>w~{S zCMYw!&%oMXFy}M}TqkPtxjdeSpo0S@E2aB5cylZV#b+#)n>NOK-u}wG?=NP*lMovrb7 z2KsDSr#R1c z9ob}hJTK~3jeBb^{K8`X8)pVuqLv@)SFA%#|1_?Vy6_A|%R2n=ok89K=m=$%b{f{& zxbqCdXln0j!1FH5`GbMbAN6>SY3xTE#67s@scLI~31eYD%RaNMy?+IK@^UR*NnL4# zA&-$5?%N;4bI!Z*yua(z_gWvo?=GXG^nssFe&J#K?oI8y@d5nq>Vx|xtQB8sK>eTp z#tT?){rqJ<8&iQB@c96qAq9T^^4ZAiBdm9C&3vkJZ>YU%`tT*4o_@`pYyCx?7W%3W zbh`)nnEp~egWhA|Ik0J!Vq6t>;lA_RrnT*YMdKWm5Yj zlxK7+(T_&hmUBM8WFCB`%IAwmz?X7@e%j|LZF$iwUHjw<;6wdHeK;1fhp`pgjkWO> zrp0~>e3y`oXK3)_oT0+&{J5VQyf0v{+k1|O><*^8BLnXxJlTHuhz_r#jHd*V$350f zc-FT0U@qn@6&cm(Egjuy1fGcZMI6h7_b&U3L@%?<)YE((X5v}ZJt%)sgyVXBr&-z( zZO1k4_NkWk`32G~L%H2;Sr6Ko(=eVA_e+=q)s5r}S@M*wjecF10}JZp(NtAgS;;lF zo;%^YlxWurDc-am@yRjsZI|S+T#7ku`#mEDXP#R?EBt%;IpqY7|WPmK@DVbP=@{MShxl_GWS)toL@(Px=J`ko z2bl+v3=T3=P(8;%=1j119AtV&^nQb6;2fmuJ(s7)aFA(V#}GEnSPuOQe)GuVXWIh zrW$Ya_on?B3?0gs=D`IHm7ca7^>rxiAXDYcM386X0Qd^(L|(r6AhB$<-T&aDSIln) z9du@?ytG#lt|kn}KkHG)B22U?Z(3iZQ~5UKOKU|<)FhiqPrDC|uOL9Y&Yu<_b(&2L zz`y}D#ij?^Zc~|Q=aS3O9Av8LX?R~{s2O8XGL{8|p=Od&Bb&^$HZn~*$h^!1 zS-@JUk?TB$?{uWw3rf2<8fN&LKtGI9wV7(Z-LeS>nXj=x7uf~!ru~tOx;e;HU&=uy z{iyj=MH;_#RS5@~?~>~2Aaf?GkaUpw7_MBTzSFm)-ca9t&aQP|nC7VMQntP&R~qVj zHsMPvBg=$?%s-GgFI*J!a=%^)11sr|KtmnWun4p&z&No2uQas)U2gDeUiV4l>oVHkTP`70mCa z_H{*R_p+P`2bp+fWvGOM%-@qrI>=myN*srS%u?|G_i~W=DGF|Q)e9LHv+79)nToCC zxWM#g{E4v+>C5o3_70hz@glphyMs*i70)er0f#Z4!EbR@(n021Oq_I(xrZH|bddQj zW0MXtf60uJ4l~7gG_t~Vknn`%yY@Dhl5P@SqGWgYq2t42iuby!QyXdi*(i7%+RVe z=^*n)=9_eoxtgu;MGi6*9AsX}woN+7{4?1k9c13c*w1m0c`uSX4l)%SWYXiXN;=4F zA(?cL`81g)9b{g}7D+nD9K_h9gUoL-_NyMHWgwDqHDi+wGM{1nx;w~JU-SG3ZSEOa zpYc4~KItGcgRDwDsbEFDn?c(l<#Leu-z=ufLFP;B8kd922g%OmAafVvT@ErU8DFV$ zS0i_u8I7br&q3z3NZ{-3!-i5x7kO}rG|o*AGzQKEeMrGUCS3rlq=U>HmjBB-$b_h# z!m)~OdI{}u%Gqp`e}RL{Hk94xILQ1nDsJ{te?bGLojC_Y%Fl4>_{!{sq#oN>=3!K^ zkL@dSJ{wE>$~=KaGRsj1U++O_X-M4r6oznS9Y<>L0W=uhd7tRL4B@Aqa2NjXLHhLG znQ2?VruPtrFnua&-+SnAgdu&}R1~83Fq`t>YPxvxZbYEZ>wN{By)0#7&D?L2<`KrvymVM4Tn)a) zXfrabv1S>F(@7Ku=p4j+ld0jH@kB2@V4mo`9RI^_?u)QZ#(300dqjge%(-qifRI^ydnHYmO5D;`NFhwDxHb5xt&GP1P$!LQp6GB3$0t2WFV zbXm=dv)~Q1)jluIf-6ZmpPy9*4ma9YEF8rxjm?`(Hm`ZnsIh2WvrS*5Hob0)p3Zt~ z(rL$>Z`sD)Qed^j>4s91K}=n@I4rIO*35;5#~8Jcx!rI63AvkdN9`uF2PCg4 zqxc@*d{D^zQSHq7Ax)YKMm@>Mhjpzzo2)D?L64Xe6SZmQ30XDEt}x7<=3Tm6AEjq$ z|ENDUAJFt|saeZb8s;wZDNVMej^cM#=HtRGXVjA{$a7{4(J5$6G!y;Wnm;uU>JUCy z7$0Xd@AYP*rsGRl>pkAvhT!}=46pR8!v*6fkk1~^4Ltu1i+_r@6g3`y8PoUovi3RE z6(~x&mme9AAJ1GCczOSk+lV{J_mEva_;unJ{1im6&x!c!&%dGg3ptTiHYl^$ng~#v zcJ#5%wokw>??y1{y;#9(-;4DD?Lb5Jqz}Kv=5h7MKKy7M5+Za9y2uG6E zTD=S(1%xV4)0meiA&j5gg4B3n?0d<50cgs|We$TAjRB0;f2S3o+*2qFf3#(G$^a7Y zWuv1{r*?Xe%IS2iecq#HI-PpyJ*uTMv`YA3II=vmf}-*vmEaj#hW|vihL4@I5huD_ z*umjM?_z`u=!R!|TsXv778{I$b_3h2=QAVkJ_Zmcm9kYXV!+J|Bb7sXE0N)Yv{7 zeLfPIXQhyz;aiXYL>G@ShQA8h{1%J9+MY!?gYZ#=d8wW?uC`~5`Sz@FwLNPr(6h$X zde$)Kjs6ez${JlrBYY8BNDWhLUDGrk2WO8FLzw5^#uu&e_qh`j@3Em3SFJb}hnhOL zJ*%w^Evcys1%n|te+^ZYm4y~BUz%IDtga%rV)2ri(8OSVFhAt@qW#CpvUG7-bxmCb z9dn2kly+(Bc&UZ-INog`BMI-eI7f=|ib~rxt$4acXyp}GE{BUn1f#={#P)9sd2{^R zf*~AcR8&+{*4v2^JkigX9uSY-f5SBM&YPH@sinQyu zsWe}M`WqT38}3w5lbi{6hsv#q@UEM073Emhqh+ki$4;yXsS|>8o@nu%6H>psrqU`5 z&4NSSpsFoEwbU6~3ujwZ*A}8a`_G+F^=Q#-GT~(k0m6NJGOEPw&fpUs+ib7TD zku|=ys-oPGXY|SA;8r-*Q)?E!?%VX5uMtjjJ#)<#b;x*Ct$0;6`U3EDYvqP~jj#D` z4Z%yX?>;rKU^Y4ees9(5M#0i5-^HG*`&TKyZ>lFUK>cF1?|JpDHNL6nu|L82?d{4p z72b2vz}w7MRcfWG_XT{Hyb_v?e*XO`aD!{{wkq{L^21ea=&(P-H}RiWA!BR2ikn+& zt0#_yBjOj}rgooE0IuJ=2H&gCR&RI;e48Fci9fo=6E>@!gooy>zM;NT)w9?57OG7u zNKe7hRY86FhgFpt`&Q;oLd)hCO*l7SrTf}cLjhWT8+vQvYel}Z)sI&-s~c2-`VbCr z?;IbxtWf4Xxi_7s_0mWokc@rk)Pnvw%l+zb4DTB_ThRCjiqn@#b zQBzlG%s&5ISDJICO`kFIT&4-)SGTy%IRBjUwSO_Jo4ff09>7F$gn={q`ffDwv^2*R zwjFaBdNvw&`Yz~ez{m1e{-P1150u7zwxi)WdfN*$6M zj_S?Vd8*I&CajL1#xD~q;~e~)BOUWSmk!k^!bCX9S9qQ_yE?H1F*t# z4zBpB@$1M0fBbgfw;R7(5Ox^9TKu>obdG<-J1@L6)vmbOz(TELaa~#UvgHPrB3z4o z#%(XWyTX+dTr}ApZI9PUFYaARc)wE8%~@{`H@z#%Yv?-n$})Ebblkb3q71HLmo>t{ zXo>AgR{L3{r&`;GaKQF#dMr=9J-zd4kDJ;BuDI!C@!)Bjz#qwoz0QPGIum@#R~GMov2Py<%}~BfRv|qwmsX<#gL64XV8We;HT45Ev|m z&?Cq6>B|QH`yKn%H7;FJ0WV;66<0!vmX)EIS5(0n@lv!oUF9~=>1u=R))$giq7~KA z{rfUnd)8eFFS+dTCHCkyzP#gKEhf_6SXY(Q)Rp62*Y?p`QdQSjR+1P&brmZcDvjmz z_N*JcWa%;#AEk!VXc3u`MtDM|gXI~mn^j4%o#s9X*(DhSLR zU{o~I5mC7eIwE)r8|Dg=12Zv$LZK4iT?@s^igYad=k<%y%*=|^N=-}CugI(hhAAsc zOG`7;|MR^2U3;x_V1_f?GQU~B-#O2I_gd>+Yp=cb+H3D;zwfFQ2WQ#NfAQi77iUXT zeXE@7U7Keb7rN2pvMXQ0IH#?iG$cArLu-re9^v(d4u@;Hc_}YX@hTji-mvfBx?NFQ zwZcOZxX(47gj@I)qq4Fqhr;Obw!@<}sdx#lO5;HnC%&qY9k!;iu4)N5o$lTLva_?^ zl{gLT=s2M8%4w;qy8yH3mWvv~E|BdVSXvh~S=fpWf<|A`D{WZaSheH=RMs494LZldrll(~eFO6tT5%!3 z5g!@?=dx)T3KK8mlu#udY-@Ni?m0)n;@`Ty>Vi6NNs}~BINo^}${H{)td0+OC zWmOzT(W37yf5J`lRbGsypIBV#6)%0)xfk~Mr2(GsJUpXI;2S4*qJlY!1kM@wF2aNt zXJYP#$3eXLlKj~8nD$VW@dzoQLO*3tQPOgo^C_m^Svt5j(^_c#hphrIzla^!Ka4a{-O!M4eRO|N<~{NTuwEhQVrH!7~a)LG$i zQ6G=b==+f73FnY#r(--Xg?%QNvN$Q`_9Ts%j&!vq5s>;Azd{ip;cldBH3E?77|1)h z^&5lwu}xV&&ZABJ_|DAK0h?axSiD8$h6WRNIb>JgInMFo4PT`1W^9{0RB<}=rMRKN z#C;C3t8W4H@wBmiL{q zXbg)gBI*LUfyC)Yv#5Ohc6?zk#9c&}Fk5WE0D;}3m6d1n{zcT#z z{Cfaeyan4PF#ZxF04clI6y;B5#eXJCzBx<&e_1lGTN9}70V4n@Kb$4=dNYCX|7!#w zbv{ig%TUlGP zxEaq5u2}bxNLIYBT`Q)b_4SR*TI4xGrtFv@@Zkc> z4%F4*eS-0kY@VAjbJGVoY~hhlo~B!nu%U%7PLRB25e2>_sjO{nszOxsKEiz%h36`~ zbUkfz#YYt%Q+!hKX~pLhH!HrN*si!u@kPa#6kkz% zP4NxIw-n>JCk!n@|0RyXI77r_bSQ49Ll47CFyi6x-AEh>VpeeqCTYWPQLF^1gfEc77CE`A@3b%lv(03CswjZu9#Wo_++@|vF zigzn+RJ@N!T^Q_!rZ6u!AjC498By_pF4K8yv-SAetRXk1ce8okIYZb3i{G8(Lir-XxSdsS!mixS7r{bR#^Ee(LUVO0vi&Pe0 ztdOf!ZdMdutk};_5tvVWu>xiM1Bx$Jpp0w4pJ}>I#lI-ZIE46QEYu^uSRsoqR^X|+ zUwpAbu2EThu|ocm%6BP>FIMaqU#vj!#R?Q(tiXfOzgb`L#R{CJviM?!yg+5~#R^$` zu>$YY{SPaOFIMaqU#!44b-(yxg*+J73ClTNQGBsN7GJDD@x=;!UE|^3N75H6@-rFc z7hkNvsVa*vR>=H#mGMoA;)@mXbt-RAyi4&xMSlOue47>dy$$6z6!Xx5Dbt@HQGBri z#TP4Zy6zWWtdQ$f=65`&ro@u;(Xn|LhlJTQ6yK^y zf1k>aC_by$p?ClWNS2dQEKxjOkxMT!{&R|VDBe#*9lx#eV=6zT@^eJA!!K2CSGiN= zmsI|%%5SLrZvg^2 zL%M&H;`55XQ9OwEF{Gz&ah5k_BT*#mY~x|4=of6>Mn53O6sb_zm z2RqP%urHqa6#Plim&d0%z0_wB|8!@pfbsix_?uR3r448t{%8HvN$@9oS!XEk8gwQYF?_O-_5m}%WI;bZXNvcbb_ za@bAB;p_D$s0R$W!nq#qLo(-k7a-@t*>$1uN%lU1fzxYS>bHE`ONlf$iG?3XW9I5zWsue?@`@e2ju7AtxlZG|p)oD>`Cjuwhpnhe1b~ zH@E}S=7YUWpXz*a1N-<3(wEtf*|($qi!$L8b1VC8)Q>k^_Jfz<(}vfDmwExd-F_y0 z|F5iv_RH}ocP{R0^qpId-(wgI=A+$Nm&T#+!3w+KF}TLYr(m=1)NM1a4VGoDkLPfG zq&Lx?d45Me_Vc^fb5TF)83H~1pojU5F8H97xH80h*#CXVZyj<=X93d1R$h^jb_?R3 zem{<#{ylIEk71j>Y@mB!zfGzaV+6;HuOe+6`tAYJujIk~Lw|}=&MJ%>i2HwtTL&HR z3H!$@fd$Yp>Jlwe^}v55{MHTbfWJ$aI}GmlG<_|ElmWLEoMm z7d*1%m#}e1uY%t}*m`4@hucO!9{<0ve-P`RRaf}bk@Sl+J$zjRaU9djswalLFJU`5 z@tXEE>9y_Do!)rT*57M<0pjV86K#z#^Sa3QuX|q3$9cI4<@o7YThAVFW`_qK34?Gx zQ~3Go+P0iMV%F-jzt^^A5RS*+c+G0R;KbFQ_rZ?g7vsF)xQ60a)^^?2wR{oEep&W2 z_;j7N^(5G2(}(rPF#fGX2fZDgkyxwS#{u8MbK%Z@%-7X#yy`OXTvNnscZK;`dAdrM z%l=(0KWb~(^apI?Tf#P$_FXqa+P4V(>?pJ`+ZHDjH~(W z@U@LG=`Xn6`YyP z&`sHAN!i=$LD`tip53t-x>$#AXdV2G^uIlL>-SKHg66?nF@7b2f;K;1@bQ?}wmwl> zfG63uHe5L$>?ml4Z-wTzjw6t^_As{74KS8Cd&?E@8|JZoWzTMt`&0U|&O1-QacsJx z-G>hhxsPFN;r(LK)bdV@ds~mtvG<6dT(@-!=1ToA9@F1z-!$$I7^`{jps$Q(>c$v& zBig?Jej%hy3a(BRaEwJ;^H}_iv$n?Du8y&8=KhE};uXsWJ8kFfUgtTuk7ABD5bt#Q6{H4leND?M+JxgxSGVWGhhl*G z&p}<>artTV6P_z#8e_QJQ+Vv~cY*V=73ZYWTkuHdG~_e)*u!uyVjqIj;Xfvh>$}xY z!iPG_8_IS{&%yDv4LtnFf$axkACG$|&T|Uw;o+PF=|*n|d}1e$>Hnz0bvK z>~X`dCfhHL^D4)lUh^>9T;{{+%YN`C`yAT(V$=b8rLFyyKSG~5wplJV$QP7hp480egR{5rynq9^ll{BzrZw%n-c6hLEnC~3+_y7W<&-Uot;Szp#z{iDj{TKmEiuwjI;(Jp4cQ?JmX( z%$*L7H=oBk;F_dQu_rpy=mT<{U_O#YnfIf9=33`9PM>Ocj!MY&?R~0q)eX3|+n>g{ z{w(H&zxn-fZPy*(r9OzUr)}V^BOk{6Z4RGlI`DkfZ~ns^2R6t!aG8!%9K-kwC1X|k zigvwD;CBtz|JIXa-q@2dpb2_82EZ~Vf!CG&c+S`!cqlUv2IEV z<9jvg%JU!2QP+gmueZ80U4Xub>zV$e-+-Pt&NchL-!@bHeE*fYah_jUhc+(2+#ctH zbIZVc=J#IP4HyenW8H^>4vq)xN8s?odla4@y6Q)m#z*~VR@9F&p8@zh%RbXDT`S`g zeXy}FvG1@y9meYh_X@lV-|n~$#dT!bXdd*$z4Nx5hc(v80Un3PdxZA=*Ws^?bN2UZ-LvQF z_)4aeb7FHYsMjz1XeY<0pQBEu{u@zm`cE8+b4}l2`J1ZRhitm86JsdObK_PsPGQ}I zer21k+d2qo3Xi{bYk;xG$9)F=R&OXge$5S7JK%;v>FYlAV!?>cL0;$&`7x9;2zuG4 z{DNz@_CwpV+#%56rK;NDJ4)^eUfFu-|8fjGZX6@W6uiPZIEjALFKX|#otvPO&nUyp zaYMh{PcWz8*tpSqgy-pxeVsh?#p)vu9{c1xmuBVh7Od&~652C{G+)f>3-o{W2-a(0 z-{hJNpG7(!V|t{E$Ku}4yfvN2qI~LF3tg{G93j_H;LYE117!F5l5?HL*bX0ieE-9> zgT^5*ec|`RxH|0Zcb+f4_(vTDs6TyBANTNx&ef>TTj}+fhn*?+b*x?V#6ZU5nC2fa zqVrnB{}u7f$NRpIxEStL?_lg<8a}(?*)za%V=x|bBHqUiy!b~0U&lSF2xWxhH?9-h z^NMf|Wb9iv;{eR-F}!2E)Hy?c=r{9*zy3S(Ir+~h>o2&cuR!J_x9)x;H892woKhSsU@cqv07>T&yi1XK7wG|69;GCV?F%h}-bXu4`Sgp-dEpyK-!IhNORKlIClruEm&i0bQmD>~rQtsn0N z=!;wEZ_cFu=bO}hUZk7v2nS>Adj)H7@%|Ra8d_c%#{UYu%Ul2-$Iaq%t>2V1*3Uw} z<+WqRr9LUHKZAM7)7Nc%1#5KST*%nOHMtf*UnF3$?i1GTvJ>Jy8^FD)n4|G9~__=fH!1p`s7d&q2?)9VQ_1uqIS02w` zKkj-w8)hAK#rom%yHfQ;HbcCOjezL5N26CY6~Ugk9?V9$9?V7?-(*GBH`$?~OR~cX z;oOW~(GDkhFvfT#8|&0I>H23oLTd}BW31jdt*wgVZGA#7ViR-+;?=F#XgrLe?;*r7 z?ZdYL`f(n=!SE~bbYt9#T^%4v=vItwl;BqEhe!^$V)r4DaVypjxr|$}U{qgzKoX=* zM7q9%x!HwVu`pMh{xOVOv0!xa+t3>f8;Q&@INo@Kl65N~wdn|#?L&qQ;+=?9q zAr`t7n;>-&w_@$6@Q~Qpq|aE_typYas6U?kG87sqFDVSZ#^oo^L3R@-ZpC7`xD`vF z{G!;zpjw_-8tR_q{Vv~I;JkSVqkw_>w#%GUX@d11>& zZpE;0nHM`dtWZ4p6ct5o#aNeEkKBs=1BK+p>XQFrLq=}JendUlZpHqW1zWdbSm?@| z9}~A?G1skF%ylakyCbYk ziao;f7P%FR^9)CB#fCF3!>w2>!>w2>!>w2>ax3yhrHbSC$Zm~|^w$-LIB*s)A(-HOd)%UQQ#^B8O0imhX#Teo88`w*>Lu}|^1tXnax$LGbY zTd_l#*SZzEig~SDG4Uj4-HP$bj5%(_rct%yR_p;DgyUB1Fluw$iX|y;?^f*JRO`4E zy8&wQV{j`bL#uTwHj{O=ZpD7gY&&x+24nKVf3dl&Td_N+#<~@o$VzwPR%||#_Z|Ey zUJ^eYgIh5=DT`URV&pSp)~(pbnA*A(!;~AtRE!=XW21`-UnE+$VxxKGTDM{+Ft2qh zHk#GWbSoCKZpFUJwzqD@Zeu|UtXr`r>T=wQ9nW~jt=P9%6UVJs8`U{(#o8F}xD}hn zc*m_+E#>aqiY-HeT-}Pr;8u(tL}J#h*y}96r*6e!a4W`Z6{ktuiv5BIy${@q9fG>| zHEzY8!-4yK5?{js7++%irmj!uOYE!I%nN;q9fyQ{#g|xoAriyAR}1vOz1ILhUOeCz zaQ-joxap9)@o|m61c`i1?nWXX!wZ8TpFi}w(CGIOKU=Zurwpry`;|VvWSBSf7Bm1P z8ARs{qdS{EKD`RXt}h)fes1zZe>CR|htH1vM6*A4#2M7s-`|Fk{SOXDrU7B9^M=Fy zRzaBR>=6%B_x>skr(c)@!enzt{12lBN)%i{jd%ga;|~f`o;Bi5N`)FZKDCD8fg%pS z2(=jO(`OkxTR}q6L;SDv*v}hL&vXZcm8uv~%(gh#rx%{!`nJRfPB;Cb{v${WM@A#g zVFiZyk3&RsFkBq<4EOm>6Mbr>MlpVbRChQ_85!36%;7B%M)`kak!KEXfH2yBB^2r# zAr<-mpfq=QV=bgZ{kJI18BXCapI^yBV7kNoJPKzHZ=^KV?=Mj!=orqQ;E#orz$T{i zeYoeT_(pEe#-D$#|0oV+fFJ_X8_;T>7qw1OFw0JAFNB;wV3?uqs)bXDWCSyBxeQLyKss2|g4ju7Nsxsn` z5ruXkev%5_ zqrA~v4g4k~8xE69IF5^_aT5ESw?6*C_3;s3r2gyUteZc_8@&KYH!*3;GI)Edj!BoC z>y2KH{hK2dUxC{eq_6ObZovM0Fn;Cu4PWZ{7l%12yhAxd9Pv3O`(AJ|3#?clKZE|N z2E_U0;ea@M-C^uM)O9#^Jb>*y_83kvWOe}W9_$&E$E-mr+-L{H4-_42f&uZPMbq)} z&!ZY32QnajEMoJDaMt+~RR!K4W*jsKJI1cWpU)|b|BhS-ys*a?LOlP7v)QlT+4$dtO z_MaIv!Q;!}#gj^dj~17vpKlwV-Vn4tC{l0(+#J2Rd}eyqIJj3TkG<(1RNe;fL%;Lt zCk0P>#f#w^=6?9Wfd`&RLH^Bfzw@_AB@==H#X)m%@G=|?-Iw@z@Z%LXPbwK-5)4O? zGvUqW{Fx==O5ic)Iru*sS2A%tHt&URnS8i4N(UdB8BCP;AYB~Hn^}H(`7Oa)m(08} zD49teuR!PTFPYgeF}SEcIB;h0p$CIo;g07=iQ?e874^a8iJOCqW|pUqf-9T}r7WfV z^x&3DV(WY#PL4{BDw#1UXeowA9qzyB(%5ajkJ8h0aTBa~@W>-@C=1iG9tzf9Iuq`f z@@EDYKFF3@Q7jRseJA)8y!4q2-O9T_+BJJ{`Nc zJ^3_KL%ZummH&)A9_}vL`#xy?odN8v*c==;DFA9#VUmFap zM|7V&C7nTHXt(+NrI#|7S!sH8XSp$W^$iKm#(}Y}1)x=Th|lpLzJZ1KsfF*qAUe_~6au!54fKF<|Bw;SOnfc`$wE z?2-u+CY2l!96NJ1YWyKYZbElB?dL}YkFT6bZ(QR`rGxy#&uc~LKfZWo(2fLGUo!KS z;QMge^nibJ-NK-${tGyk;NDAaUKrdPs}D9Vhcm9?HEY()m=LtgESa$o*VnR{C3VFM zxht0E`F=mo$DkVEv&95#Y2Yvs7QOvo&zkS;2P@G5Voy_dXk#s}aoo%@{2B3mmkGQH z!Ee#@{!b@%=(7ml=SpC@3T#d5KWKXYA)oNCh+Q6B7GIm-t40Rie$qhKr;*9aJIj2D z8)P$9^dB+<`(zOE4#%6s3f$OWzo=s&3=E5LgMt$z85$w-_G#li-v2ZbYVd(+qzr?e z2J=FaB0r3L3^&*XIFN_%tV__=^9aojrg(7 z&*5hR^=`v&NA=)GS=se4`CdWY7;o~K{k!9_DRL_lI*>_wjd1a|w5pLdrMu#wDbq0% z=8=t11@}XAOQc?jx^T=SE^s1uN$NgG{lmx|v9cNNN?^`f)3l5pNpf}4RH?q1&=JMC zN$7AYD#$g3t)x>OI8JG3sjOLEg(j$l5V|LE+%dTwzF|r8vewG_s)oj8OW{QYu0+MH zQd50BYmkC~Q;MoZ7UQnnwMxh%PdU&p!3hy&K7gtFfwyg7|nwok#`+@JJ zU3a_GrMD$G@oG(F>Sg8Ns6~#ns->p2VR4-Z=GxXKLzOV8aXl^V(8LTy`J0v4OLxO8DCeeIPBag0G;c&qPfdc6D)o zbWuZX>ms<8s&Dm{Nd6u9LqY}R40P!OX&KyGEml&$~rDiFsT+&>% ztg&QLI(_7XGbW!{(z3Ls1jmKx>4b^J6HDQFNJV-!ttpXdZi(tEhGqTYs^xk;(Yx10 z=+Kokn%ApY9V^LcxQOnaD(jn@T9_hduSs%ZaLr(>$#$L8$e{x#fFsaS=RucoH4wW=Cd^|GZ)QRm9YkCOO(%k-wyjT@!Vi&x}8X-QQ}IJn7GURl3vNe#M_ zj63L6H5c%hm&lo2wq#jLo%%p(XsM+yC~+JRU2r_2s@e}PV~lvBf-@^PZdzDr9K!W_ zPik>pELAP7TCxzAX>n~GoKjY1N_2)*T+_68F}r8+iF0NbSGBe-ZKz(x3p>{UQ)$fd z@Df8almgu;jhX1c{7n40Xfp4~4`2&Z449Egex}2W#XD4GJRnMV3G*^Huq=jg_%Z#? z(xs8c8Qs9 z>SO#0MYI2Dq~n_~6PS)+8Gdg42BCgs*vI;D#%bzzHMY$$VbfbQ-t%U2LxYLC9I~r# zu5-No5oh$>jBS&LD!u`IDQ;*maeR;F>YERJn8`~0h(_N%*fx2v>HRPCjd6CE&2MWu z_8W$OPk`@Lo>&b`$KMYXb+3tGNV6^bid_> z22($xIoFJ$Z>&`)fX_H7{CK=PZnlZJHn8bEb{y($lt6Yi865?8|Gw~@6cvmO`bJ9X z4)$%DQ@DY?kTQn@Uyi#yN)-Ao;%n*vOkD>O3o^YO{ZY}Y~H+l@vq?#%*c%AYhNQX{+y`NVflR4bmQ-HWD`T&7rvD^E}Ot=AFjd+gF;_P=!Q{X zJjXv1D4%QuAm#Eb`Rpv2dcqwM}e9)|H!JI8}m1G_}m*Tl-VO=ZcpfWkEMVa_Y)KU(?R^<0#+&@JT_Fp2GDNa+Iu2`;Ep*UCZ9L4#H3lwV=7b!L>HY>I& zE>~QsxJq%Y;+2ZmDy~z!QSm0l4T^1ww<+GPc(>w4#rqT=P<%*nlj5U_k10N>__X44 ziklT*P;6J+rud@bONy^3zNYww;#-QmG0OQ@%u~!)EKnSzI7D%%VoI?{ag5@4Mc5#T z{!+y$ikwff+%iS^@(A10RW4VoP@JoHj-q_lf$aq<*D5YjY*cJkY*k#YxKeSI;#$Qk z6|YrXr+A~{O^O>7+Z1n8yj}5b#f^&hDL$b1km4pq-f!6+;%^7IO69eRS1R5Io#DX%K3@~ih~rz-wx6ZRXL?tq&P-#ykc6hRB?*paf*DtVENM&rz@5#Rw&L@ zJV$Z9;sV85#YKvZip`3xipv#ODy~vot9Yg2wTkN$Z&bWVaf4!;;%$nzE8eZRQSm;- z2NWMt+@$!ZBIjdlk0FXf6;p~uisE+&>Bg&^RxDMVqIjHQnc_6X>5Aow6^e5e&rzJO zxIj_-8KK-oDmN-NE4C^wS6r#MN^z~?m5SFYu2Z~G@g~I$ifxLwDc-Jlx8g>{`xGBg zd`NMV;-iX>DL$$AwBmD$n-yPBY**Z-_@d%VimxcXruc^9TZ(*8mFr9~PcdJyKyi@b z5XGU2Da9g1F26>7;}z42;vWk8>GzKD$0?R6PE(w&_#7Ulh8Drbfp{q9CwMI3I5h+h zE5w(Ghk4@vi2n7i%@6l)-Vca;uMv@N2p(|h7w<|U^1VVtK2kW8A5&~sSqs#Ch=lG6e0M2#i@#?DW0#mNO7&=HHv(9Ks~oB zepB&b#h)s2j?Z+Rik#C^&f~lTc%WjDqWIav{%Vz*6~)gU_RD+>D1P>UGS33uujyp| z1X;e)2X^Xy@v{e6<~u;~vj-GEdqDBC2NXYhK=HE&tciv76hC{A_?011{OkdrR$2V)L4Hx?*A@Gq&oI6C*#j1-EPnPNPgPky?t#2OW%08I`4W}I z&mQCrD&M8}prZKML%hr@fHHpo$~*y>hYrko3{ez6dyvJ?9&ozu7e9ND#m^p4{Oke6 z&mK_x>;c8k9#H)30e`LeUs4o5d)VKP<0er2>;cE9EPnPNi=RE9_}K&2YJ7|08pZXB zUsAkR@e##m6+0B;7;9LrjH5vDvj-GEdqDBC2kg{z;%5)C_}K%BpFLm!##8DMKYKv& zvj-GEd%!6ge}dvEisEk%@zs>kSL+oQYy4G;-&5>V%K0Do-S$KH^^w@gGoms>aVy`81X1seGQwRVp_senj{4`$_7(Smm`U zuT%Ui5#JWvtnv3KKBV|##pe}YRQwl_-y~q*Qk<##S1Vqp`>$8LP4_>d_!Hg#GsSmw|9-e{Qvbn<{A`rv;5OvN z4#IUvgj}ZbG?f>qT&wa*l~<{Z%WKDe>Kb&x7j;y(Ge31lPcEI3lCX0>b)~qsBE)$w z!xiiu=neD=jP2zn*j|>=cLRLV!6q^eTgkkF4PrA}0Ncyq@D0FR@%9Oww9TZ=r7vmL zNgCR9#@oNJjp=%AC~<9<#=NL8~OdG|+(U%YD$|`YarWxX} zY%@gfGLASrHS#VBGa=c=n?LOI$MLqv&l}>2fM#!r(CRRbUJR?luOTU{4!@2>eX9N0 zRKfQy#_BLWs&6Us#6zpY?=YqdtHUr?d@GC0>vuoZ#z!Z=1-)@j(qIJQCBIK;6l=%j z!Gj?bL#xB9aF2~2ItDSYIy?+Q5Lz8_4fuFpu{yj4n?r)J$pm9vtHU6&I{Xq88YwUN z5o9n@esYW>iPd3{i`C&>$W;_fOx})~1(DSuP3VHi>hKqgi>wYGWc7|5DaV+fO}0@>rq$u^pfb-7LaW0d!|E`wR)<5F(OMm*Q2$^j zR)_bXR_pv=Uf8mc)gfP>1d-KYBNatfhpbD`Bdf!H(3Tg}C7)r=$m+13TC=SVf60Qa z)!}G}^8>Lu3|yb3lH5|9WG%j8N0%G z;R098D_q02FtPcCmol%lIy{2yZLJR9&%D;^@G2&@R)^nULt3lD&ob6p9gb(LwK|-@ za;??jhM@5wpNF{G6Toz@B>usSRG!eaSRKx0dTVu4^HdtV-4hOOxj@4l?;~lHRb6A#Rb$AWcIaY_K zGTyN|JcaR&)!{tK-B}&hBSEfKhXJe(zrYGxtHUp{{GM7JqN^UnYZa$StPXGDLGLZA z!%whZjC&amK>6`FeE-3sFE|nqYuJW;hw$mm|09mVztJCw-3)liL_0yT*STKoaOMuB z#nAO{AcudZcgRPX=^|!olT6IO=?OCNi#C75m_86HLJb>4!x^GsgK8MIAKT4X{CFoq zig}~>zYpws_W6S6TzuWT&^(@)#m++$8=M_urh^_4ndx9#&2+GZZx%x{9VCvdZPxpK zdV2Yq;C=Or7RLJEs}W3vGrr9q#KanSg3MaOW~FPE_sqp}c`(j5v)g@~?VKi1{yBZ9y_?@?n-VjZ#@+R$aGf=9!!NTdFItMP z|M7_e*IccuTDl7h*=Buk9errcWjuHMb+s&T)!4vqSiAUodPko~@6s31yY(fr9ahjY zoQ|>Ei_4ltWy4Q+cjpV}@Y9p*kDt5w+IhP#o3U~*zd4o>qr0VHceEZXtS5T*`LZ19 zdt-6e-CD(Uk9>IyBWm0caAz0(h&P8){M;LbxqW1Rdt8bagnUQH`xWyu9p)d~eS3T* z_PK#+8F)WqdfY%Ibd^qKJDP^Tbo88Hg4vvk9cG(x|A4?dr3ogE-`+X9y>cAH7*w9e zfoV~69CNU3@?g`uG>Px@oE>J9JejMn6?a}97xfWMyHsP_{JG zH=5bs$d1u?0^7M1Kc+3hFZ`y{-a5liIqgK|!g`J4$CA$HknSWn1wBJi3ARBD{J=Do zd5!OsCr3vHHR8p9V-;CH%EFTar>cCK;`xe;6xS;9k(l{}CkNiHvhd`PA68j-a>&Ay z13Pv9pB3}ap{VCTMd8UI3r`Ldo*XDVIZ$|Vpz!2C;mLuwYySHcg(t^;;mLu*lLLh( z2MSLP6rLO?JULK!a-i_!K;g-O!jl7qCkF~o4iugoC_Fh(cygfd<@$$`R?1BE9C3QrCco*XDVIZ$|Vpz!2C;mLu*lLLh(2MSLP z6rLO?JULK!a-i_!K;g-O!jl7qCkF~o4iugoC_Fh(cygfd<@$$`R?1BE9C3QrCco*XDVIZ$|Vpz!2C;mLu*lLLh(2MSLP6rLO? zJULK!a-i_!K;g-Of5T0e{o|jC@52C2c@Pl;{t!iab>aRQD$iE@nC>s(^?~%fuf=fh z=Uj)fj3x;68XD z3C^Iup*a8Zd?&v!_K(D$;XCPMO8L$K=%wI0A3~=C-}x0p7{2qXjOp8l)5JLV&Lfat z`Ob$J(*@rd=86x8B8Tt%ABdLkyr0sjx$NiUJLggi`OZr)zl-?JLm(*M$$N|PomXR% zd?$zFF8I!y7^UoIUUDTe7%4wF*^zSRJMTm;@}2jh&X(`|6U&MC&aI4#_|9*#@)6(J zj>fZmCtulGzVlIPiTKXrC`EiH9a3iToqt6!JLfz1V@At&j^k19gzx+VY9)N9v~0w8 zu3&{CzLPJ7E#Jwy^o;L31=`4WzQCLj-zk)IHsARZ7Hs*>N{Bt+J2|_teCO$Gj-2?; zcUZBA?<5s(`OYhN-Xgy9yNrwY&PQ0@F7TZ{XL%9dc?Ij)1>d=qF}MCkTD0o^VdlB?&3S!P$S_xf6ha`QG9-}w^z*!FzqY#xrocb*S5!gtC5VEN9Msm}79p|7db{h6@8s(jm+zd)^p@}B@}0kAg)QHC zE6d*%zLVFg;XAM6LGLZTQ(TlAzVit*-wycBuc89}$NZ6qW#CNP@SV#YzH@m7-^u4} z{|d>(44j@AzH^Pscdl{x&NYVb9Kv>s_|7s&F>fUQ-@|;Tg5^7<%7*R~;lGgW)CWVpx-zE5p>wI8d${^< zB0l#G*_q*xYBxE~-Q)etDNguWI;ylPbaDoNO`)Hr9#fn; zM1+)jFV#tcdiR|y;ZCxvTxS|D|M>oo?*cI^h&Zl8RmS8=!j*~!n3iE2es~?}?JS+l zbeKT{_3cTnb2<(pSFUrKsXXdnHc!JgUXMnh+2Im-e3-Y%aaIFO9wyF}>s$_fDWqpR zlaVs|_!7(2N3L@M_L*SfvboNiociS>vC)^!bxwr7lu-sW`qp6EJ>E9xE5JV153i0R zec4>+8PNA63)4V-{N1XkyS-uHr=}ReiMtcq=qt@8Y97+%!gaPFT`pW_TaH}kE8sfG zmz!YP#mENT{riIJ#Orq__;N1zUOo)I_&?&cWMs2N+#6h{VLw?Wxl5Q&MS=b0@JI^# z&t%9(9_Fx_Hv!QTqm9xF);UXooELy(y_xX$;pdR=gxtV_?h&OB(_ z-nMgxT<1C#Y`M;n5PQINX4-b{&vwj->wJxe6mgx;^K3-6ohMR?xXx9q-)?iAKW2Fm z*Exgr?1JmOjpapLXQpjuCfCW&8XT_kHHbNIowq>$yNm0rMva8)l#drI*Lf&g$*`I6 z!ojY%9oNZE-n!;G|HfEr+gV1@a-ICV&fz*M7~3V+*}ZKi7oh8!>&&w4%;GwEWp>AP z9>oUexosy`&~v%Y5l|ysrwjmHa-Dx+ZF1o{`ALhzb-thZtZnCJ#&%=dnay>^d61Ut z*N=w4%a!J1$D!9c2b$cb)Lh4*x@=!u(@34KDX^$&$F@z zxK3WHhU=6sOZFDmDJ~=p*V%<_=lwV+EK0~bECb&P7_KwraGj|Pu9FT1{1VB;40FRw zV%s^+U<36mfb%w{&%+30YIdxGnvooEV$@}29R;5u(~>bEDjPTGl*yJG$RuW+5m;aE*D z{lLfu-Tl2>=k{DDzlkt`{bwS6d`Kz6FXTF-Az!)9Q0A%^c1o3D)EVtgK}Rpo*~@eG z@|<{R@8vmrY}2{hJZJcohB4{Pf#;k&DOa9z3`jbJoPF5#+I0Rm^PGH_>C^F{;W>T0 zlvJLRUMOJG`8Hx<)A>b27@qSL##o!q3!p@8I{(d>E^Io(T=9dT$l*EffM|KnuTdJs z7l~ric{;_A=Uju=#u3k%;ydKfhb>(Ls!b=oKhmaCJS6Uz=UfkkhRe)L(&Mg?@{_HO zlsnJ)C*-0{=igCh%X9vYe_AIxnR3%=hK`I>(Do zKt*%lIY~UdyLiseqejAWKEtNAJm(^|lI1zYL9r3@3dOqCi1~$0%xi5rC-MARAGSwQ zmE}3bfZ6h#FR>x5P3KiqWqD3IA8~9tSMZEjo6cuhko95vIul!)PC7VoY&zF4ueIr< zI}^vIlUJt8b6!o=jt|=vJP3#9oK9_yO($m}JFw}T&cktRI!O)+&nW|dco>TnnS|7G&QMtA0{4x_; zp7U8YfVJuTE@Q1t=b_ANZ90Xq>yqdEJ=@;ebT(0y!*kB0F2|;mAHcgj=WVQs!*gz? zI>(3Ydd538o$DFz_^|yVv^qr)=*zmb0d*>4FCMCEebS z?QSs^rjakKstkFms@A&Y4XtTd_th+IZNd_;kvS(_!@@zV?htpg@)x-#fp|J$-n`+)nIzoP zu%y1pl^YwD)VXr~(yGN7JDQhOFTpOTv`VTl1TW;SJxFoxsx4>HdxYOSt_S=k{nMCW zHnaK7T8BT}6Z|Im(i95Kh2NxotO=%m*|wb5I`!+0-z>?|mXmyGF8t={(DyhV8BAdP z_`6ln>>rY&EoViJwwz0HwB@`hM_bMpVas`}sccr=o!xS5M&1|vCb@4Ld=Jf?;kTCY zq32?mx)z7owA1;!OcQrLf(jz8`X`AsuHRC+U-XI)q?o4k9ubFAo| z7G5vC{AMq|*~@R@IkA`D>@mMdyN=zm<7_cLw?m3^r*@o2P0873?Q%{8@b0)&AY7v} zg*7_QN#D}xwXg47fOR)x-*4~mD%!Si9nL|UuIu!<4kxg`*S5vm)ZVfB<+hIf3fi_5 zR9x5jQ2SF`Jm7aXJ+`T`v^hkZ$A0}se{=wKWR zULjJK1tsCxx9>=Hx(fFj1_~-JbZ?l|; zQsv9`ctoi_%<4sy>Z`2(QDMG<cwcFGrN>?bMPY)X}DLB=hm%GLVj$7Na2xVtQ9++7wlen;5GdC9S?Xhf-!EsAGQs_|%9 z(Cb+EJz>5f$vE>xlzUY6 zs&R^zQoWU;rBumW$E};(53pQIseYOTSxR*P^IA%EIP+RJxu-I(rBrDi7q@P5d1c0( zWkF|CwL_`WxpLg0R7o_&9ZHqntmE6e$z8xIIh1NM)a1vpET{~vmQp>5#al}C1ZL}o zQYC5KKaOQV`?6Y=QvD>=SW0y)W4obLNlqF{HNGe&&XD7lQXRxt2BB{+m7l5rPk0?ya-;-qIFAg`lEGAv5gebYNfh{DfVJQit~(~b+~_2 zuzY4|@X_Lm^!W6Iag)YPC`p%;EDHLq=FZ~M@(JTg!d2{njUQ0)T9Y*~8UKT6w z55dH7e?=HKz5k+5_`DNLfGkz;+R^R$<9DGsOpkrMG7`ICh}k<$=w^jeS(P)=2b;qs zaDPl{vvlEU46NQy<=E?gOUteNQ;xSzwezbF=j61D_ zmlO>!j)B}J)9)-DIV6(@n;!LD?Cda`Q?t_XnSggp6HGe#ZFP2gH{v+BTndkaYn7Sf zn1gMT2b@b_zeDR~`PdqN_BN~0x*fx2v>Ae9$Z#p+LFdf5(@N?_;B=n`Q zkNU{@8GS9-cJ-Bk7obg{2_`O^FMi3XUw3@*N1!idlmU&tHQ09R_ZsvSU?1y;EK&XV z1k5}ZYt#oQ8$D9rG z`XX=4cHmm48&u|X-n-VR=iQG^NWcdgT~oqE?g4cp@di!77XSO8u*rTjH@`Y4awHTmiU^ibyl@X#ptk4)do8}A}-9P}q6e0cth zf&2Rh_+^+M4o!IB&X6V^2GM1Wi?N353HjS+8v#1mNtfD=U4Da?0?(C4j?D8m@(RXCDLC7xu5HUd)>~feZ z{tAm!b~zZGTm`*B#4cY+Y1CX4DF(ZTQ4HDTn=$ze47>b(2(ge|enIN8A07VPh~JRd z*yOXw6?56;Si~-Wm{BU_B`-$?BjqP2I+C!@vC5*iP8xZ=+W0{Mfv(Wg~WZ7BxleGO4>*#4fWgu^zF@L!d1$R+oI9IU{zN z9{|L%+2uACY}sXguP{F*>~hRymt!uw9J^yA6_3tKj-twlU8Y@KEQ4K+MeOqB%n`B6 z|E3hN%YWpVirD3IDMjou9Vf>kcKIomm%%Q_GT7x<2D==K*yZO~Uc@ech4su}mtzsT z{B4%^czAr}$>}UFVwYc~6tT-Eu%4D(CP$hByG%c_?=E(k9~5};*i(gHRmrrIs%Py05 zjdjB=^G;^ixG!ZE?2Q84!e9K)j8}k-)_VlcKLM1JM1#w zJjJ?Wmz$6vS9UoDcA37nW0qaMi{ zfnv%c-uTZ{&3AkLWKm7EoUcK(Xe$wI^FnQ*5Naz4DQPctAWv1qJl{&myMX`ufO&3N zT2r#5?xMzq>IoBzCzduZty8fCyJ|{Y#GOJ`ldcvd$B~=s0r9nOi4XaCLEL=Dt7WaHP-|o7yJpU%J zZ^5ib0=JjrH?9AmPxx2It_UuVUzV8Oe>vafGpxdo&$ZL~=YPV#Ja!qXCGYA{ZG^Yb zwb=K`IvcAE_GBYUA{G@UmjjV~=6QjF%RomNVI|wzbWx2l67Hp*_nd*TX=9STLh*yx z&ykGojzoSp@Wypb@QYkkJJIVUkiiXOgxQk>vT+npw{+=}Ca-2u75Yzg;{{A$)U&&lsv|QBLK&a#-Yhl?&@IZnoA{qHg$RVcF89 z7;-CXuxDY@(iL9ivL(w}>T2b~6@13hKvo?Jo0fq!7qfm!to)u>mA2PI^By!`-r-ub z?#;v8QnLBz{DVDj3~nxi@WV_c;vuv7=xvB|1Jg2O^U+5*_DXRad~)P*@I5j%Wp0?wN3X#R{>w%Q(wNQDu=An za~4y-E3j?yP{+N{m*R#76SoGktM5_hE5JV14_P98*?jb~h}>ol66vUqzgran621xE zFc&`hWu!yVQ84w(=A-vR#rQ_l1e1;!PMX4)DVUJ0!cIOTLbV(RkBi63IhhF$fR8@I z2tcFH*(ICq{(ZqmoB1dETnx41aoFI?w<9Z~9bx<`N9KI=Zbyzbv9UM!YK|wY>vH_a zKhr0V32gUbBLFEMhu<2OR=h+} zUdti<=TyF3@qR_&w-GP=Hcr|u;THGrz*}|ja$z&d(-h|@%F96PU!w9##rqT=R{V{k*hM4#n=0pFT%+E> zipMKHttc<45dWgeuPX|_js1n_JIr6CI9YM3;u(qy6q^(;QM^uZgW_F^4=O&cxLNT< z#Wxi5(1BV05XBP36BMT_o~>A~DC^rI-zQbRS@9mlrxbs!_>$tk6#L;?VY$L@1IMU* zwBjj>^Au|pTNKwQu2=k$;=PKGC_by$p%}-&!*WX%Pf}c|c%@>e;@=eisVMw5@(;w# zfqI4$(FaEoQ){)bfFr1%%zzns?*^vU>v4~=B}fLz4u7V;P(`p$@0<@$Pf1Bw(C^Zd%TMurR7iu5(yioqn^{TRE;*L5W(VT# zNjqn+?2N6na}K~iuLcKB{(Cw&YJbzTt?^B5et~ygr-vWyt7!x6dFwmLfBWFA$$dw* z&)(b)*i;vKDSUyJdZdsx)yDprC5C0qk+P^e0M||ZER$Tj;oTR(F58r4?5%Stqn9h+ zMXr2Dv^UvuhxR60E{wXhv36p~kux?i0~Gpkytf9J0HfO@grxYBvh8{G*y+n4ttQuW zU8yG5`=hgh6#p)gf)roI5qk%uc%M;yk3pV3mK5*PJtc7R!l9@okKYNbzS_y@(Y5A2dfIBE{!ZI(noW|Dfaz)Dn^6{U}AG z_&2E~lN5gy+4B6vNy!w;%OJ%QmJ}~xMoWs*<3wU7r1&>ct95>2Uf8k`DSj#|baq(T zc(R3xB2t`nN%V*m9|UcAiMr%(m@^{9@r>yuvPtm`EZCCb^u;_sA*6W1CB+jiDW13^ ztY}{HFjh1o#ko{km8Am6yMC!BU1bh)-!_?Pei2nMwa(@czor_a@I406i-B?_%znjlHzd1WRX zQhX*=JEZuhc@PdM{yu7RNb$cg{r05zAv_$16u%T|@)ID%WoXSL#S@kkf0MQ8h7{*p zmi`Hl;z?G^lH#WDB z|6hK#7u@P!eSTMVjE1cBMwH1iHIM+ z2U5&i%>RABkMDDKd)N3Im#4?Ou;_u$m}W=D))4`Yt{|jOw`STqFwOp{=ZgvUq|?UF zm{mTZWJU??vXf$$&4|+RFwCAfzI?(g>=)zgeDo5RE9dj93G6{)o4qy$8|?5|mvP*` z06#a5BQI)=&=*ev2ch5C1NC?b;7y-7aN46F48_O`Cz|qh6}I;A+v6GbxX4Mvg05R-t*4A zahKSUe{c5&Vs0zh{CIng_SzXQQ&ApfH4?J<@hC#$O}aE>vn^2&$j_KyHm72T*=F3e zxGVB5YJ!P79y@R+jlvr^j_Ei^zUJloWgai4k6t!EzCPdczGjpl-fWV~a`lygv}Jzk zBN~0x*fx2viTi$T_S(~-FNO5f$HOuD_?+PCyB7K;W1k5oE}I`;D`C*lX(}paZHW0*-=od~KGHy#EAE+pt zHuejr4HQlrD4aG>IBlSC+Cbs7fwyb^`xS-L#{OQL?XZ1?(?)v;rwx1^V;9eJug!MY z4)1oG?N{=`dj4C{*lZ8L&5P$zY_@?%P{#FMtTiXY|mgZ#Wd z({@|N4ajMgWxRka;|=}{n(+qnab2iAM_=GaA9GZ$-1XC}6mH3^LoauoOdgN(lQ=M9 zU2!r7!+trLq|q~Gi~B`vEr(ge%)tHPeIvaY14|IyZ=jPlY12-@tSoc%3U9d7tP9D565nB*FQ$*1b6*I zL>TUx@3f3TH@NFnynsUPdOvPJ&19;z+H20X}RnF z$9i_bUHgoCd?eDxMwchgWj!PAnijy8yS|2!<*v^_MRVY;-$Jr?7kB+V)JV8%dP;Y= z>(y)t%U!p+VqW1jY#9@qU$~xmyX3A$`c- z`+&RtE$SX|*T2MpgS-9=_I1Twql*4meZGiRK2a^+mE8(vjw-Lr>Q){cAi|7I$D=eFU!v^DsF z_dIRAtDN<7?-9=Wr5cHs4O6iOXV7_ZAC4!0WHCJz(m%2^)=eJP~p z>*t+u))SG&1XETvXFcDkAKsKk^~>h0zk~PrDWeQ%^sT|Rd%U&K$M^57AF@RHvN`Kt zBeDa}6DCj}f43@{{hRQzmi(m&rrbNR1M~i7v;PgGL(x$%>9RTN15vSDIO{Ty4Y_dE z>(F1OMy1+zadye3sSkyH!CAx9&k3QA@kP;|(8oBRV#46oZtrl`I6V?rCOK$wzb3H1 zj5Pv~@{#y4?E?G^XC2LEBFb7P!SFF2&405Nrq&07UcMUFTQ6UY=P0y;*kA+21{)|g z*ucRUj~FjD*g&zt2KMsR+HN3N**;=}4HO$}U@u=Cw#y0(qr1mf>j;;t@A0FK&Y7=n zGC{wX1u%X~zq>V>vx6)4O=?Yge!5D^~e(OUY;-(=#QbNrQllej9xcWb_vi zVb-qx6Jyl7IM%MFHMXu@{RU#XTDv;T6+Z}y3>l5JtM7nl*RK8=B}+zgx#mPjMt_2@ zu@4=C%H+p`aS(Lv>i#HP*RK8{3g_C@PdBD8VsYWW5tgN)v8?P{)Y?yOyX9>gB3U0u(rMPzg(n^Qe)mUHuFX-5OH2u$2tm951YM#k|7r zvSmzceqj^yS~5CKmDZ5DjCm~?y@H7?8BKQ(&f3*iG1ijNA7ZQ}qxngkvv&2fEXb14 zuQRbFqx(>_Wb|6*wPbW7V=WoYE7M)O`f93n45`=hARID!2DLec)WwvyC!?qHa2zsv zG1SP~)iMBBGI|_~w`BC;%+`$|HAin*yZT>DXbq_^q8dv^AI3^|V@N#-$qgCJwX13J z?yOxc3u9X{`V1lyEm(O;z8ogwwTNRTTT&9$rPQ^i@k z`qwPKr(`tOuI9CB)~@C!WqWPy>K~xOG2>l4AJqi|eKGdw+SRLZ{O;P-!%!E0@2p+T z^;-7w+STXrbVMxk9gy~&2l4+-3+d=Z6xyn1Hql5OSyS)8KDx(CSMRa4tH-fR;}@>P z$!`-bY|*!Cx^#8)hK-4KWE=eeBQ)e{8@&Ug=-s0{qf>KXRb@!yg=}BCmz0K$ddpHe zZf`N>w~MRk(b7mclhS*KOkUKqv^ABvj5XKbu4=3li{h%5n%0KJb)m4du4P#xjf$}z zHf?P$f_vzunhUTBGk5no7~k^_#=AG9hVz7Ic-T3UY!dhS36K3>pptLslp8C4ul*{n0zXWMaFlA+P z%G;d!;gF*GWpm0+(3djGfJWaMY`e$Hb+E|^vVO=C>C5JnKZ(faOlLwm>f`TLMSz4F zkdaf205s)hbIPAbx?DKrZy{YSobtB8xpB&+=r1SipnlFS*);X0urD}eo=Y2iZvcea zXooLz(M66NZDM0@*g?beSOUu=N6hiV1ooH1jR2%P89%12z)v~lXu4=9^g{9CjUjg?W$u7zFvS5|Jg>A6P-$8^~v-)nv*fp#9mS5Mb z{uW}oTC+OL6)%M%!zyFV>PI2kHLIVbG-@u26ccAIF|BJ>e;yOhXw7OmaMd-de=2nm z6Xz?i$u+CxJ0sV`*{n+)PyP=S8YwR+ACyF_avIr9T<&XDe+9X?X7xtaIbxONlc$JP z{w?Do6Xz#c`N+iiA8h=HRlbc<#410}+D;iMtuZ7iOJYQ<^4F;)lU4o;63Uv@2QxKr zz`T}K{t**fR`~>qmQ}ulnk}o$E7M)G`U6z$u*%o-ARJaXPHhgW{0FAro>i`4l^j-? z45_SHEdzjMm0zMd%PP~~d3S48k3wm(W;NX*J8M?+TN-E0YVxh!ty%pxOYJ-O)xP+I zlxtS=!w_f9YO;mSn$^Ro+_K8wVXS49KS^DdRsK0+EvvkRv6fZ7n8%mNDm!adf01oz zS>-FJ%3+oFXFVJf=Q74Stnvbu<*>@vP@TgnpU!xPRX&CB4y!zma(Aq9Jrd-~Ds#>1 zmsw$J;(Rm9?txr%JkE%YgY4g{-D_gx_0L6Z<>;P!I-da`EqSfod1M{w4xmLrDQM!00PNRnhn%rob< zCUnC*|A!Z<*0V?=y0vL>wV_>UmfBKRgEhyOEKJECKcFS`Sy!71ZA_O516p0xg0*p( zd{NUfyo#)BsrM?ImQ>a)Z)mL)PvidqOJ?Kg{6D!!HD1b^-qNzmEOHkY`%Z=n;|tnc zpWAWK(Rz}-r1YLArH$c!w@$uy&PVrJHvgZXp?AnbySEO^{Ul`b&~d!47=swT3dIdL z;vWxH8B;O|+Y}8jEkiaB-Br3Y(waQX$8>vgz3TBejtU$kkE1&tdJS&ww>cFto2Mb4 z%ge3=Im-Jz^%0?3uo`IcU^D0Ss=59(+kpDIVq2I?@{P{(q5*Q!>(-e}-U(4o0D@3o4;YeN=Z8z{Us zP4;;^YOkVgi(l5Z1=a31C4B?-pV@&Id;K0tKeuH_>P9RJKCmM%wYC#}cKQ`SWq z=;syO*f}VDBZiRbcI4UePDNttNGw&5$9?IOw&tbRcKQ|H-xj7>wKe$v?0pM-RmHXb zew>pWazco}K|~%W1S%*b2Z(@3H3}#qV&tKSNJt<7DkL!pBG#)YsMM^I9N2^vXwzaiZEfCULwY~QCd0YSG|9vxS_MUTck{lkQw#@Ih_nI}2HIF@e z_P1xv!>QWg6mRN?itGYs0nWN^>*uC?Bt4It4`UnSwjKZ6z56YozIJ>Q2Sve0a0Yg& z^3gYj?k)#J3H>pbYmmxMmVCUnd-t<;SU{UW%4?|#!08Q+H;YX{nYttha^A#QPA zU))C)^^5xeMBWH&0j~E6_hF4`YecpTSDSYq+&>)Kl;?8xbx&)wXYd8n?G>*WKJM!7 zINW>H{WzM2M|dB}_w`ck$USh+%U7=(m57n3_Z1K*)n_9tl7!<0q1vq`Cg_ zRg^MWa|7e-LHuJhH#q(ulrlwgNvC);JYPpLG1s3KC%p92wCR3$*fqCU_CswtR|5Mz zXw$EQ@atWFfv(v=n?971&eBLpiO69{5?AC`Yj|9^XuZzFlL}l=Ync~g`^?c z^hgU6Zk&REy$7&afL-jX!>)b%uxo#d#4D;^=lEaIOe}r~MF!gRNvy{-ZMq*EcDnyMC7GJ)`5zj#K~YKdZT_`0M0?HQH43`hhmx`#D&nO=pCD&+*18=|>jz z{ZZJxryxIFn;r({;j1R_$6)Wz7=0qMaPRvm+}!7phL)>rB6ltQxzB(bh(fGS!y)z^ z+&F2X*GAktC-y^|$Nvydg`I&M;kiGb_%4e+$)gaj)JdF*u)fikM-t3Sz{V1U7Xe$C z;BFEAaT2#UL2bGpYSRZY?g`?XN|Z8is7?P2D{{K{PEJgqZ>UW_l)j-heI(PJC4r|W zE@48UHvJ9;4z=lzG4SaUxGFJ;Y^Y7=l6ybYrt{78t;4RVG4w5M`eGKs(x(53{+2eK zQ&;@X+VuaWSWBDEgP99`9Cod`)=-u%ru)#QAIU6Jh_+rS%)9c znTb2-8*0;kLEliDUP#I5+H^lW?D|I5WvET(%5mS)rgIK}Z)wwc;J06^Qua&R+{8lq zTiW!E6lZDEPo}@6O+SwQmNuQ^GJZGObZ-d)<3*s ziV+qJu5W@}1)V>n!EujJ&Q(b3EhI2Bitv7*n*J|vo^uZG9~jlNvv-=R_eQbpsDCcA z+xDtP90-l)VN}#;3e~Oao9bHhInlx5XS4!Y{EQGln23T}AL&l0DLWmfL65~D(0m{caTT&uZS}GXT9q2RvA({g4r_ON zSdIKaP%Q7gHo21?c)zKC8|p4>h2FoL&8+`*x3J#3o=@xOrK9R$9dk=v9Y)bv$2ilu z6|3dXt6p8x0;aw}bz}#ZEdP5fS?S06ms zxwo)w44bJd?kZdgkxd-RVYpn@Ec367Up-@t4B|`6*(rqXkBbCY4nj1R7az(_YnBHT zIu5stF~(Tuz`>T+-&zw)IYwSB>?RInY{fXRtq(KxOgL-H8*Hr!?oLv>ev&>p<^+5_vw!wb*% z-GgF*??IR6H{-ox-l?}4E;J>S?SSql3Oz;Wxk6V8-6ZsrLT?qiMJWI9DCcpZ99Sj) ziqHQ#@C`yQ7W#nD$A!Kwlyk}{SM7%lsuqWVs>KwbYCmjHwSE~??S~Ee2Kp_=`>Rl; z=LMfY+oAgip%a9jDD(`WONBNHy+r7>LO(C`9-$8leMacZLf;VjH=!{!V5UD%Xt~hi zh0YXume6{kR|@@<&@TwRPw4YP)%s<~SL>HS)%s;nwI4R9(({6j!U7QHW0KI5gf0-e zTxg5XjY4k{`X!;?5c-7Bp9pOi8bK$=bSi{S6MC`G%|fAkRN>wh`mWG?bmr8v#)TeB z3VAEpuHjA$;Nc_7e`@lmhV1&+WdO`;Y6BH)kNVf6bJxF)VJ7hTsZd0(L#F=Kan5LW zD%*DSr|xxYTYF)$&Fx*>hTSFG4)LLjrT)`(n%d#F1Nu?_>SUW=2tOxvMbt@diuSkk zp-UkznSwrdJM_8K<1U3BH`TVI2s+Maaodi(WZRCV(49i&o6JU59j7QZeOs{^33Z?N#K+FwYsq&1_H(lka zJ95T8Wu80|MWemkY4FCab?$@Hx9ZQH zM6Q&|Q`Kys^4!P_ouxAriE}kosPbf9_Dtnj1!+`ydW;#UJby=SMTu%2!2)7#lkA5J zh^ZOQuJV+4TIH#kSbP>m1}e|9Ssh)dJim%${rhyhf$@=y7pOdkGY^5v^T&K{fy(n~ z`UNV_r>RJC9^RC#hKXjdvvA{74_9dCAg9rGD1Ag*KTfy(o1zkRGwd;Z>aKI;(`qq5EHmtD$hK|4ON~eF>bhkxRG%~mFKHtEO=Xp#%TtH03-JQyFG;*g@o=T}1 zsyx3%F`>#c$xL^x^2AohcqFobm@3cN3?H76TtGHddEP+bp~~}a`i3gcBKn34h^bz; zRGv%e8>&3dXZgBRdH#@fAF4cgKDTeFJk{J5OXWG1{?-EGTlgp}mFFWAXQ@0drN5=} zTt|OP<#{FfZd9I;&mzE{s65+Q_)z7^h1fl*JWoRsom8HDSB=W^vn=%9sXR*{8(l=# zDo=kn0(dVm57FyUS>6nncAt||@&;esYqombq8 zYFBsu;1O(<Q8O1xhGgRt)0!A*2mG8PQsPk`Hpd2<&Jr2@w-zKB@s2Ilo2 z2p_u|Y@pcf@CKbYCJP79ft%%4eU)U zr&G=Qe$&9NURB$b67@i@?JaLv-_liHdRTfW3SEo{$#0j@!Uh{mccF$2Rj=4Rx274o zSv)H=DKr<qQ(_@AcQ!*~Wa} z(6?%3>+GT={~KxOp2DU4uJo?JYopG6QwcjSquKPv6&xPi?674?=@1lyRJe3tm1E_$ zLEZJOXVomViq%l(G}kmNubAvq*R82*UQySuY`t^xoCV2g$q8i@WfjR$&2_8mu-bNX za@4fO<#qG0+;&-A^ZMiob5Bc-Po<7Nf5O;tqs=+8|Aso<=DOAkYF0Zvs&rNRVs)o& zG`-haJiNE%VPnS#=S5f#{V%g?HT2=F*sGk?C52QcX*aKGl~v#C>uRv-``=mr`!B1! zHJzMR>o2cauX`x1h;~k%Hf>6B6gsHUd(=Oj!14K-x|-Tmm}doR6y4XP`O{B3#mW2k zP~v7g%XowSQmDK6h-B(;2SWGBZx)9f0X)>_ksxZM@ceGkJfKN%XIPp{9d1|Q_$@SX z7?0toi!jDmPlSW9)2|ZWKz=QaF@Bjk+!V^eo#esK_hamMCM{x2J!k50n;^y@8AExz?htC+ z564?L*)W#h-LRugH5spY2$xM~ow?rke1ywIhx_?M(PogL1QK9*m{n4i)wE?;ombK5 zr_dsXWyof1_XDBBrOq}qu6h<12M%^_SRS^d(D+J=Y4d!Nq48Bs*gR`FG(NPp#-$3* z)Zr3_8pC@02rl}iaM8aE)EN333|qF$5w)%Tf!CYCG@C$9n#$H0f#}rPUI;n_!dPw( z&zII*TZ|IqkRV-SiY}m zI$H4k&}q|sC@FBjaG__5`}IOUC+@ckeOBDLZj15wO|x~n?7PUT{sI1~f5FXA_G65P z(}R}Pv<52P9@p58pE$lE*EP2Np}&U-bi4`lrcUbm4z6c)pnFB~1(DR(760z;-GiLe z4V;ScW+Zj_u1M-@%Kab8y$x$&FYn+Q*KT#Rp<33gLxn;SK)I|79VI3>*jmW^bfsjh zj}uqxSbKfMp%~{iT*xzw)=5?>yW^|?b@`bxI!OYfNYt+aiADYIlFK*bKf$>%zg;`x zj&9$7T|06>*30+53=+$ayi0C|I}s$xsWR@Lc{|yqou(3(dv0FKILDo?{HyX>#>4&+ z-OtF!vWS_=d2assj(dt&n;mzS4uEMP4G0;F{1W$Gv^vk@@|>^pzP|?NnpZWf=xRW0 zF9zpKd+jtfhB{J@#(y5gPF?w(*=XDt*6Os5fH=~aN>~7?<(ck)?IK+IjesTl_*A9x z(KkJxoQmkZ9Rp$YU<^dPgPB{#1$)Qs`A}AH?3UVZF5HPOaA%&=m@HG{!?p zgZ~%!6a}UUOdB}%mr-0S2lGWdYU04^ya?t+TErN?S>SDX#pqWVpYkw0Dv-x6*2IC; z;YQ|3TEv+8TLj*gHxTmp?V~)poBTDxZp(WT^6m?UyNN>~ z{MaRF5o7!=1#insS@{Dj2;|)eJL9Bq(eN~O{S^|PZn$m}s$KC6kfCh7?{O;XmIQg(gzpoh4s#|+VU2|fnkg>@(gdN z?cVc0s+|+2XQxP>xe1N0TB^hsJmK}HfyzpeZ^*~1pWt$;CNhsfK@-u|a7CKKT-x>PX2wf?Z z?+D#D2>m1}s`^tx9}~*w>7j}+{Gj{|<}{S0H`sG!^B!Q&qbE(w6?^W|k2nK+V$Ps* z6EJ4g4zvioX;tl!)W)6v2aHK<>0@5t`;9T5V%^{}Z}%u=$J|p;_q(_mjLD88k{`L9 zPBUD3`@`rI_4&#IcIp5=A13hTz4YTI@(3PP2osLikjwY^d}I01Z-FQn>&|d#r%}lQ zV><4MkAiip@_@WfET>f=%#)PsJfKLr#UfAO-iub}Ib0tCEQ$8lHJ1E=NFH7(>Tx{B&Q2NniV#%(;jX_uwhw&IL7cBWQ!to6;#>kig2YfaHQwgRG zTu)f?4hYy5#0v8?3vRZ&?O|p*yG#z;}_24uRARHBIM0^g z_|Q;g4_5@2#oSZ9`d{uV<=~xoumyy&ByBz1n4({ED`W&S2Wb*Qzz>u!(_qW}< zG0u5NhvO{U{+{DYGiVu;K})`1c)wxvE6xW;Q-UiEP!g>JJ)+Mbdc2Taz9IZG0A9RP zJK|0ddAoMJhFrd11A;+Im7EM(ew6{P;O#V*$IU-h&u2#E|R*0t~4-Siz9^B<(AP{2-1;Hm+VB zfXJTKJCC0@F6aHGAJH8^fFTEI7hbMw(Ef7L*gyGyD<9oB@Cn~ z!R6kDn;CG-0Uz!{@Nkgv7D|Zu1b`tv{3kiVhxanTUwA7E@F7zHKCE%vBb9emUaheb z9a1-~(c!bOdCoU@{~+tC*`B(FhhyDS%d_uyXZJy@lj%i~=|!>Y+(D|?xH%8ux(FBX z7E+;8)&&)eZOjJ(Aa;ce_Zk1`x>nG)lg64gWX3QLJVc~A`NOZa9)@N_oWOpcrUIAU zx^x%kIGOXWspI44mu;Hy8IJr&;SH9Y4c;~elN<#{hVLm{3Smqf%Ave$Sn|Y-aNh>c zx5*eIgP0Rp56p5fZQxiA)`2O0uMqNx)tEn619>g5+wxXIUN&q+%xTLTY~`=el6O^xyelCu2{&U* zS{vbD%Nt?kuLypoyq|%carpdb?t;8LBLqCHyzUSRqRhQrV9C4T3$G>#=IjEKt-P7K zZRmNj{!=h*WGOHlk1U2G=U?~1#d5ITSym(!n5l5chS`GvVxfFvde&{*DKI_L8nlGY zx@_~d8vj^^w%Sge^4M}hvp?or^I6lduOOBjpg4VFy_ruKK9mA3b#HCPJ@{@6M!*j` z-rS0OQm@VTFIW9{)A|5z<}mj%mCAL@@?G650=%i`UUDAgh7QiNG;=V~nlLu`!JKc& z|8f1^X=qvjo~8tsdkt>cp(%f1fkK7whZ`vLP3?#~hb~ckI;sSW{yS~H<&zAcMm?vw zw9_zo8M>@g{(8P;<+F5G+VJVhS7= zs?@IVZ|B6{KfwCo2dij0+2&Z{W^8)A&{n*s+LM=b=?2DhHZG#`e57mYAbBX1V$wmgpU@f%Bd-RZ?S z2AM<{V@&x0rZ20KB40-k((C)@GIR=?TTw_dmGsiUTIiTH* zY5o@Sav9V79U9JLlPIW3_b%As)nvT530N*TbQ;1vXd+qiC>L1@%yd>N9Q%WGheN01 zAux;r-^P1Pj}?8HamgN`vDL*DlkW41Ik z1R%4=G0jO8Ij`?&imGRwhB1q|hof2Jv%PHx-me0ul6Z4|y9=E;>bJnziZwvMpuV$s z2SfyGfc}non31jbBkbb#|K++Lr(t`g+qR=v&bQmcwLe+TpzCTOP=5MCpji4spk4)f zcS3dcRDn^j$3?-ulTSofgnEG%E4%(u42n)eFq?hU+6a~nWtm=pKhd`#Y~RNhPSc~FBe zXT-2TXu5K*ieZ7!3}v0$iytd@{%poCh-}9_u5cQCKLo?AZ0cRZ*2ghzzNpM^o`w6& z(G!->Uiq<8WpXDmtBqmp{*v{m246RMoa7--``55tfs1Go$-{MEDv`iEdvHm-S( z^z_RcvC<`!od#ml7?b%u$E5o_TB{$`fgg+g@I%{dxYllEfDg));La||e3`G;&cWS$ z?=x|2SK%^oEyH~#kfzfjn~7^bf`s_C7-Q0%0tXv6ZUe({*q(50A-dbwjWAFnFB8{p zMqil=u6-NwMp^Pq{Vjrkij)UvciEXbi2qsO__y9qyEw3$Fb7n;#?T{H?WgigWTHU=0u+`tE4+^%u z2>Ik((N79xA3{uftI!{bJJ%`GeXCHmhaAUMSKEu)d&IoQjL&&obvpnnOn`Z*$4|q& zm2Eq?H@OGw>pHlSZ9DK!UEmha*@@Qk9h@`IR&$aT1*O&1zrxfeu+uFV4QiQ*z<3xAUc7v1E zd;({~cHrqZ-#5%GJ9;Y+-U%~v%Va!QR-}48KCkS0S0IIA4Q3v}AW>|-j-EDZ!15+K z=KIwkz|4Qs9PY%-f7FiK$z{XLsPF(Y|A}E==520(nVAz{=H)me%AKPE%+JThjbQ#x zpQ?NSH!y^I?vmJY3=*qAi(^39^Hq?!n4jP-Pyx>9#V*i2Q-Q;R2!S;AdPNR2rv451 z4?d<=uTs~T`bEc`=wCRieC+IsSjlOAuQGpK*__mAV^XtIV^TQqZ&uIq8(8O^@ijGo zg0GncgvpMtP1~V=Cw#qpbzOr6gb(;is(nStQu%=Cst)!XrXP5G{jWRM!ER}QGRkDm zb=V-7u0MR)wJ{vw%t+z!4NzvAP(v7&<|teY-&430!k9R)s`XF&jv8b9PK1N8)2|*( zrf!|?#OyXQA$GnWZY&3JgDFSmT!+=@#@~X!F($1!aIob~2Fzi(D38?0tA*Xffz?@v zf{&s_jH%}(;B9%8kVh;|dB|!YuLX8n-Ui6ahE0jtZFyBz{x}w3@^@8+ye~jr5^lzr zv^K)QmbcK#UlII_JU%1F;q#;UF63py;&;H$$Ywa6)sqd2-wi*AXkr+e7=S~z@@8W3 zk09K`I*PiRv`F=rS$kHYP`S)?Sc->`Z7knPA}t*yi&2_j4`RNX;|5p6BlA_`8easo7TazDp z#ORZb&UvowX0&aX0Hdq956;|n&bM_0m!r8BxLfD$L{N-BU%!i(I+D6>7v6oiFIIa3 zU}sHW@3wwU>N?H^H2qgT3|$wh<*!zD z-NCxwr)SooX^ty&nm}=$TTX#Fn0@&KWsI0tv{Kqzd;^ge|Q$M!CUnh{d(^@jVd&aPWXBI`S<8vQ`OE-gm zz;*>Lg(th2fMFU6f_BE#;kfbvhtqqGe@#jE9f+$Zb>QLjhkv*_>pTp*g%KOkl;C1} zHDCQU*lnZwFgP56%f`rUb|BbIxG|QULE3PAf)+6*+;QNIonE8C@J%ts_)URBFXITx z_5WKydm67EftPQm70<|<1vgtB$Cp_y%FD;adLh3Amx%)_4xP%5kr%_5D`=el}H=*e~KRCY!jBK|k%1cMo`!Zh5KERAu zuf~kLVE!)|_z}(bxF>R<{_h}09nJ8dDWUsoeOXT!cq`MxW7reyTNh5r=y~4n9-OQB zwtn2{*w@U*z*sc03B1cW7W3LQmaSD-b}!bQ?Z5dInYi^aRwypKO6r{(rRBJWQ2TQNDTwW{6A<%+=*M?){Zxk%lFw4rcbHxk)FY=+yl#H(r#sdTNSIOR2-+g z=W-Us3Cdap6#6JaYy9~fY#;p13-Hs~&2)(;F)L)kCcSk5UaPeE5#)_wr5uJ!Ke8ua zGZ<6<(`H?kW}Yd9bQ-qp0%FQ<^82(O>OMnARoUTW|pX2EWMz3sgLy2FK5H8G0Rg^Lov#eJ;j+wf z#$Z!^MKE=Nzz90Q*N(p=m+v=$q+`=4-W-EX`3Z5=?|)hE&JH{ubTO$j5N$Niv=a%zCN{v4k^!XN$ENqpUh?FrG^Atnutm zVf)~Zr>dsk^<0H*7*Ab4YeH)CW#zHqv(?PsOk8XCpRCZ?oyJiA3fC)e0gN&^*s{+{ zFb#wLodB+h+y&4*5D0f);jGm4o)5_$gt&VSakGx0+AWOOi>3q@zp$!P0i_<&Hski{Cg1lVDP*LKdPJXXp|Tn6JxkE}tie49QjICm?`v4>-*4cHbl_jPR( zD{`LWJCGd_nqY+@qp;5hU@7JbI2b=IblP0*@5+5&xyNhf-mmab^9TMy4C}PDb6j;F z_I}+59BT$u&FNLop)@vdovBI(90RJLqyer3i2$xkK>%D|AeV2*SHSfETsaupHE?~B z0v7T%+bsd1VNL|N&Q$($6>!yd1zhz+fW_EN;aP<3r8N9Mh3&(DNW0x1_Ibx0Gj8no zD_71g_t%%{sj{8)`oyTlu&&UsUeEsWo88ouA?4}1fW3+r`Ms+ER$BS%4vYR1@^ZnVaTG8YEXw^7vXwUzi`F1qE?9Ia zD;0(835zbpLpX>qI)uzbz<&k4^(8sqFDE(=cZyWudTAD zg6!#qf29I7zM8sbSETr|oo1aF*NA@c_Z7I)Fed^>Po^6eePJB+ZO$jby8Z*wsZfvf;K&oI2miUw1h5?-}_s z<;}#Yi>>^1hf@ckLF9r{k3a!)!Ku{yu{^0vk87?b9+UHVtCd-CD)#xpJX4OFqF=!t2q(xM2zvf$ z<{k)+)5BcUqImo`bO8Pc$B$`iWPsxrO^F}7596fR`b#8u=ofmWZ~fJ2=K>Ao(15bR z)?dKcV{h-~d9@@l^hj`udc>pvqk6fOcN4fszNe=Kco&0<<$L_PM55v++k8!jTl8Vv zfLCeve!xKa-s2!{EboUT3cXgkD`2N#<0g7_+>a{nxv@67e3bs~8NE1NRk>GTF2;*+ zi#OxuaFq8$fKYRouNoVk^I2OVKIK>P8eD{!vJf-tq*WU|WW}e9{2t-cmQ^bn>Xs+N z`J*bGv`M6C%$k}DnVT$QqGns!P*hsPeTJ<1DIHYv?}eH_tDfI(b$|xSl;BQ$^Il@s zTXa(DZm?7)W{trk8^+AUtZl#`4?$8-nDtkX_v0+-&Vn1Uq%jLEOqdTx)(hd#5?m&) zusRQ9U_w)WnV6O9*K@(FKZU$(816izW#_NS%3mCQCVv}YxAXVE8Tm7QnV9uLD}UW# z*1l*ENh3tkBAYpvbv!bZ3ugTo!euLOCT87?aJgXC%{gM$%?B|n-t8_htL|V$&(BVN z&&O7WBTjc~re=u$Y@zdn^1Wfa8lfwNwg_drrT>*eZxqV*NB#aCLcbyITZQL8Opf~o zG}Shrw+9$D_k9D#WWlg%UaPF1!h2WXZ0CH|CG9RSEylCc_Od0J*ml;XorcNtOa-9QXKBTe3f!VrGwMtQ zeUL&`J_cA%QbFco7CMIcNiV+5C4S^UgZ0j5VcICHPAeP+&?3gP1H&6?yB~Es1D%I z0|yNHHvNKW0Kd_W&w&7g)_?$ma&oHsPu^y_w9{1L(in8cqqNS=14f%cT&Yj>Iz_ow z1+eo8_-NR<4Ym(H>{RWtYuNdmEVjoAV5=i*Nxo zG8H;y9XG91KKd47%@7P8@QtMQ6+=p$_5@WsW6YqYPM$VxN^%qydW}v_D61%|NRGnB zsdY6ib)%D`rZq0Fo7cFud0Aca`Xsd9$?>Vw(dSPXJ8rad0z1T-x|-Tmj;pW1?Un~)9E2TnaG5x;I^PEKQ(D9rKYsgc zc^H)luv~Xo^Lli`Nh1u@)Zd~Ed0dyz?=$nqbWHvlVK;ez)wv(?vSF|D;AiJ=pp`!& z4kK?P?6$lYGV*8qGRH=fR{pxfn!ksnJY17Hg(H-4RRST!Xe-I-L7&>4XI;l^9etH+jVT&jq`Fv&f?kV|B+l~?2uHE_0>5sm_xv+)F+d8i1 zT-wy-#L}3ju(<>4w%&}iZAM-WGtMfu-rnIgcy@N>%==$+5(T{k&SBY~DV}_B7C0JAkJ0#&80kqgRfyhS`GR=Nbq>0~Uy#sAR(n5|YW`LCleo_t% zvIS;fP#!Rch4BD4wRD{4+13BY;Pn5GA?g1gL%~`zJch;H1?Uly;)<*tUpSSJcM)=k zF9eJXjZ43OB3#`6z|H)Y48=zvnqR_zpFo&MG%^k(7L7gz53klejXskgDw2rNadyX@yKOgSO@x}<9+FSBwZk~sQNs| zKT6Aw#P5ed!^PqeL@-=o{60i5TyatLg^oXFhLX|W=@Z$Gv|G+)mcq z!1#P}$7pVF{1Hl-qPe6~JQ@+tbCE~apB7Id7yfiQF)v^oe+D_fbb}k`NuB=5ECyIF z{$GgX&(iEB&xsv_aL>V0m2sF;ya7BHlwa@q3v|t%Nhb=vff+hWXCV^bO+ja0sr+Jb z=EYyc3i3-gyQo0QjeQ0;?wBOP4{`fM=+yfMxGjOSm|qt^lX2?l!ZiMm+=>!p$SaLN z3>%dHYbM+{0oL9FP?~jM7yEY=RW~{QmnC9Rb+hAdk$6Sb>m2_pnu*1~K#_MdqE}iN z=U|zCkLHSss%z^V|K2aC#EawqkBQx<;|+|jXS@ekH(u%BI5!scADphFB;!9p4F6k= ziW?FC8U4QfDaDP7pGv=n*?zs!F-6sDYaRblok9v6^88($=gHuZ>Bn?>Q{z=k@9}$7 zyvq1jLH#Gbqqu2B)vF=p`&!EM_}`h{GdkYv_>+wHtmdlX=QH)^N1<2nO6SKPCHLa@ zR2ddJky(i7Mf*;IRF@I@v2^{&_9Kh>{wVB`KO>kM>k}CQrY}#k)BRJ$cV>b| zs`<0M2UXnJiMfn>x&*FDJi@?L9-j#wVB#VAHhaw~@WKS&OnSEO z+5FPyyu{~Ni1psn2=14@8ckeAX_t6YP(Oa@_GqG>{H5McmH*pOJOb8sw2oQw-IpO| zz*>ae1jj-@-%Y5lHRjTd)xD1C^iq!yuV-R9qZPQi_G1vnboxZfa3AmpZX9Og5X~Mp?}2x-!^}xm*CuE|4fgc zCQ9QLW&T-GW0Ayd6u!`7Xsh-7mecAeJ9v1_I}NCw8RGAL<-B7yLW^`CeidGu-HrXSJ#iNRwBq*bAK) zE$q0I_#sY=O_Gm5_mzt=8`TsWku6#|M?l=(wXrN~3xgpYSa@z639-R=8#hFdTM(fg_BXAQw%SN{@Z zz^xj_d<-RAbB|Ci=M2GgM#mj>0-Y8O{Uha#)?ryWzksYhkze87|9_B`K9PUm9%BkY zt@eT|{SH690C86{3-eWxS={-Qcdjnx>2@(sSH(P27xQ$TPgTs*RWau|$r};XALg^B zT-W&qIM1o!-TepPH9$J-4lZ|Z2P2oSURCRK5qVWZOKZ*Q)#bIT8p_Mc$`P`B#j<7P zHO*_rHsEmBu^d4xR|Ajbs~VQAUc0=mD@k~$bU0p+K*-8$-RJX2gs%z2q)s4%}(>67y6;?bwG;OqI)P0fb7nhaK z_Scm8lgj*4%KZM9`=w?6va-v@rcPb`=dtCf6&qLjz3bKc}P1D-ux@z`=)%B}uQ2EuXTB?^_ zSc76N2h&{FvUYVV{>iGBUC^phYpk#5yeJfaO$3FPOq-e9EE)P+ajjk7S_d(@;Pnm4 zx}pINt;_40k>-i3R;YZes9Ce72EkS|z~)pptU-s-h`L6pob+l$^{=c&c2+mC)W{V( z_s+_V>Snf_UijG^5Ln^x30Vqt9`w z`GTX^s165ZXM?RqHLPo1#g@z3Utd>)5;fFa*oyk6!d{x6L#U#7it5@NS$LiUqPdl}G?u=;*=FU6i%o)}5S^G81!{Vzs^3_&ZRL!ZBm)ERk3aT8{ zC!I2ThI&3GkJT;4DSR;1=xXt-8wQ1#5QSfXcfD+RUG3Txe56jO{2MUY9EMCoWe`z?YcK{cDv{1$JXF?)hxWzV>v7&18a=0Jc%Y+&vJWMH ztWfq%}0vyEEJfp%a9jDD(`WONBNHy+r7>LO(C`9-$8leMacZ zLf;VjH=!{!V5UD%Xt~hih0YXume6{kR|@@<&@TwRPw4YPUlaPa(0>Rm;Lsw{Jxu5* zp_7E3By@q$#KorM^;Nf)I31BB{URMmf9cQ(&JI-2AG$a0Rvu8oKuM9Dz-OKYczGfeW9x9>a}67!wCprwteX<%}_Y$HBqa={FG!-xgzx-xN6Dvly6H!SElA zxIc=EgKE+cOv>?RJZ&Iyp0q(zMJyA-@F?*Ygo z4xl{#%Ncn$!p=A;Tr@S1$Nwf{XytXAP~(0+1WY!J<#!kC5Yc43zJhQ=4P%AlKQO`t zhU(-811>Bh)7E{e_M{Gz8DbxdUw%~%mWySz~Gm)48WV#r0_5C?!0!VvTcXElxwltTs(zAO@e7T50rC1 zoht4XAa-ZLjsZB{!%5w&;xY_|8L+RwXTUFM&)W()m^+I0U*IG^(}5~_Gm<23p47`~btvy@omA-veEPd)_ zFEeknK+gwteuWCuFIogOV4j}B1v z4;eXF`bFUFGFA6vmv$N^kELI=t5yIL|D4SH{QpOynKOx%XDWAvl}}OD#WBoZn5C>| zV6)e9gzpu34fg_Co!{d65Flo}EL|h!?>p{<8FP>Fhm`p%XZeYr`3U(tBU&T>-tfg`wNw!ybX16m%9e+4S3{bn>Q${!xdzpBz}Jqw?|QENqS=C4Lg5{L6KU9=+fB@T z;fb^DueI4Pl;0xuS`0_1GE#Vh6t;u6jq2gxa1<_v?s6X9U& z^y55YzEQ>)KYp|Aa;(PJVhh|@4%UGw$6VM=99W$>=%G1oY>e?E9=GN7kLz+#9?NIs z)xvJ#!0Jpz!N<@d#?JhO-+H&XzaE%3q-+@2U)W zS3q78ZpN6jHp0P{S7GH3LwP~|h-Dau&yVK6AdfQ>jiHs-9YT%!{iuj+IQwn{fQTj& zj$_F~4FhUKXX0#*F+ZFo9K-96uJ%l!Q2Y-_fCbJ_P4YoU|DiDm6rP&ueuEvAWn5R(==q#aU2wf<&T4=q{ zH9}j3ZV>uOQZ$ZF3H_3|?*;z0m!$QKzxQ=-S>Rr-y8`Cseg-bj;@a)>Yj;!6?IQ2u zj9J&&pw2(wTCdI+*j{AicZGuCE<4TU87Q0I)heV|`YNPe>BzVseHBu?3qYJg{nM1; za-YJD$^Z=fj=|l(4mW_}zIfpPis=;PXdDKY`6&W96a-_;I-I`>9ANM_=*OA7h!xF? zTmb5S6*qW&j&Aw>I*?d?zJjz%>2sW00z$+5xaY<&eRoO|t&4kcM{IYBhQBe)(N*hf z+_?pOZ`>Kj^IqlTDL@>b08TAg_M8V``=HKzN^PgJchf47BR+;c-7eH^)4>HImQ^jDR8iHSsR##;MNZpAW(qI=D=>^ z!0P-8%*(WhF@F4p+VU2n%O#$ryza)2xy~qwFvghrTLcGN-crabh8yM4o#7~-c-7W?Y@jDL=w!B>Tmi=o+{*1g!Gvr-p<&Rj$Yv+XJG;fcV+B440&p@i_xq2_gk6A}F>;ugBvFUFyZ7TRa>?vj)4td0;V}%m? zl0R7}-%Iifgf14kOz6ji5|%LBB|@(ddV|oC%rV`9lpagw9tuERczYM@h;D5g2RvND{R+?rzQVADNytpSxJzN- zV2=cjGtX{V0Ks_#2=FWx4=CMWd8;F4om-eNa`&NobRF|)7_39O}}3?E)2TD(N(KA z*TO}99)1Bw{5%|m&NTh!!Q=l#G@r=b@54Y*e;Zt4(MU7;FzjbA20nRLZ`>0+D(n11&!i{}sKC z(p)UA);SAT7(d7IlZ_0x0|6E_V>RGkkydOqSiCB_L{O~$75sX{;zkD7B7gpPQ;yr= z<4-WDa=^U-O!uSa+%0$f-$CuJ?lIHryzfUQXH8(JR7(*VTxxso9hAEm$ zI>nVpf}0tsjSOCAyydGsvlp3?|TWGK--$GT6ctXK9uj8C1c$-INg<8GIJ} z36Q5YGEg;pCY>ml6XN}|bQU7^|K78<%Pl!yIm8 zK&RfX>K27jZJ7m0Yi(uIz%UbJZ6 zIf(BvLO+(SAK89nQQz|^dJIayjSMQl^!<2xV6rCgWw7^Wj6RVX-21WyT&{=Ka+OWw zUPFJ@NtiBT-3x~p%gD#98VOqDPMZg8W?Alpo7%{LhdE#)16^7taV^}jRl$u2!j<(` z(ATiB#A$pIhAm9YW8mW?ZgJxOAQBrH+yltSjSSSz1t*AaDseLdpXjl;+{l2S7#kVr zbSEb`M;IF!{6NK>nmC4mPw{jYRGGM#foDnJ>4|mpJym>XCb(V%8yP&W;?7R6-^NA; zT5?sQ4<%Q5d?wt;fQy;2I{XtV@WKS&Ol)MpH&Sh6FqD=HJicsdBLmg-uJ?Y0;A$g- zU(@3f?+nzB&y5U5GG~{1Zz+GZkpXKvaumh-?*Ab4fNK!;83a=s8K@2*=F$ysXJQ1y z^-_R?1wb_?)DJjSRR&fZE8Ql2vr3$8Qp)z0K%nNsUDkzozhoYC{2TWH61sXM60* zxsky*#$6<}Ju`7RGj@*JZNMw7N^ED~#ojF{Ckqq&L%>D`{B&_615O0TMh2Q~N}S8M z)xutwIFf;vdL^oAv5^5s;INUwIK^Wl1F()?t5Q}Q8EmGsWh%Pb$Y4D4yV?Nf25L8x6~aG>jSRSB0bfQ{ZHJ*NLA7Bc17;v_#zqDw*gQ5eSOs2f zWT3tYjUIVc>sOhdCU3oBv5^5S?6{P8Y-GSD$wwgkK)y$6BLlvx*vQ~bRN`SDVb`D%d&09%1MrK5Op=XDIe2=)>%~bBCRR0tmA>2`6XR zKuT1l!JsIL@*rgQ=cuUOh`_lKF+4{tzBsrmK(>7a!ZB0)m35l1uvgmx6u6wr&`?%( zJC0lMFPL@v>=hd~tr}ZCuH3(<9%$ReGwC$8JA#i>4BJ-Lb^kMqQXJ2~GlzK`k2((* zmmbM)rE(vPyAFX@MGPex!`Es9F8(qHWv4CnYOHD)~kXu*R<;7eRc*EK?)vb-l49*nZFT>QTh3ZA< zT<5G7jfbo1!!_q>Nq(?&JAFLgY{yWYwlY3*ExbxZFN{{vIY2L~{&nF}SW>N4VC%*B zW_fqDEEC`Jx#4EB2(wPxtn6knxNCySk=hC&&>ARhf?iI~#p76(u2%Nf<}|6??3$3i zr$UyQGr=ASX1fKwGlSl$KxZLip96cvI@hGDh3$GxyjpnABI#`fWEpy}1|Rz(=*O!T zo~P}(5G?3l7OaeyJsmo$QtZY4;id*=R|Z-CuUF!yZ`Faf3Jn=A46==6%zA#aWrxbR z-jzVF-8cFY*)1U0U%_mX5Nr?-?n{uqCx*3mh~7biZ#%X~2$tNNHT{{}8K{n#U7yOY z$*yd6p?A=btqJrt2?t)I^S)1MdQX<_EByh#U&K{39ImmyF6NsxmiR*5Z@NFRC#n9Cb#P zvojOr;Levs+^hpr4vsaMIIudC3NZ)SaxhkoP22J|V4{(!7g8gy7IqT{R%aXj5hv3k zhT&+I;IidyhP-09F@G4?3*@!HZp(`mVT~c&j4^)H8QJn~vGRu^1@bcII2{RjNh1u@ z$lD0JEw9bWAEE^E%p4~pVpbRO?iAU4&NLrGxR*?2fM>eA{#z)BGRH$XX4jv|-$U?4 zn`)9^&N_r6#xll)|_UQL%gnQZq0yW`C(Ub!7w?iFuUg7|_@VPLnq^uKL-axb~ z>N|}w@(gdN?cTG_$WDRjSrr9=xy*&)GYXBJI^(m2g=T-ug$kunem}L}0p0G$MI92F zPheP&ffh-DBWRUuI1D>KXWZ{S_L=SSgPV>(=C%XfrytWfrM+nf3gz_^I$Y>Dp$mjA5z1W~8U7-nn}t3sl;a2V z-zKy}C^yrjdnuYODTh=@Iod|badpyFLe~l9980?2B=mzA-7T~zs^dYQqT=&RN6I-?=q#aU3tcI6ozN?Vep=|ALcbyOaiK2?g_2Ck z{~w{dh4w*1R{0mI*4ZN;CkcP1&}yL#LjObPZ9?x7`c0uv3f(32PeT2ConBv|B|=j| zj~6;e=($2y3;npztA*Yw^lqUK3w>7TD?;BC`gfs+<0Hv(pDJ{b(3^zbE>x|rN4P?C zvSe=ei+<;sjd69BQ`%ngT}^4s zPuEIj&TvduQg&J)ulh<|6?i&O+bFyKm@5af*k=Vipmk&)joFJoAeV2*7r|ks;-9tS zOb{&ac}F|GMlRpyw+AcaRagJgL@-h6PoU(aJ?}-_+@JDR=9YlaFeg|cpO0Yfkt)Cf zPGvl@f$sBrt#jOR26d}?aT2dEXJTPIr#f3=HA^7JR2|IZ}6J@R!Y6$&kGXH(M z3YS7y6Nm8_?iO0a7{3$28#}#5f#KU_jPc9-_Z^6G+>b!~2J*Yd^0Hi+|GvbE?3;}- z^5(z+pU%KMZPg3q7Y--9rDJ|BF5Bn=D+VY$SXz|<`33DUJLBDyh2p?Ft{0G z{4)Q2w_Ew^?%($)$V(bwphn(C*zNMZVdaniRi?b=-yr%wYNFf?J47@Yug@Y}F8{ROM7W1dBugIU;t>R9J04ap>wDfo;gdnEFn^YlvYLF8 zIS~J}e3qfHQ)j)g<%DK`{L}UkaqKJj4{Lw#*j|HATCl=LeRkliDck=3SlcrRa)@d9 zUX#ucI$P*Gp;Ud+zeeavp?p8+ev!~m3T68x7QR*J9pe5y;d95#t&Oz?Fej5euJM^P zVSLVO>=zOuqj^;*qSx3j#Txtmm>U^edhJfGp?8YgfQc~=5@$7WUH#P5?H$o1+?|^` z@^RKwUNA3mZ9BsEbF12XoDbFRR$T$<3)+d7^PShC{V~sRso8f5>2+QkWZ`Ffien@M z(+AID!jVtM7wUtRc=XdDZeO<&@8+JJ#Z=}R8K?B)AxufWdnGd4F+7@SDS9g1!*P7Y zaoFN%j>hGljK0+TNpO->UVaIjBV`DQqnUmQJC0`h90SSGOfS(VJerB~XXI!mEE#jU zI+{txiad|d*3nE~gxv6Grn|`vpN}LtYti?~>Z6&~qwfxmX6gebs*h&k94*XfJRanJ zTmz${<2M>BD*RW$8rrf6_9 z)6baRGdkYvcoWlmR&!NxPDqXhM>Cy6?#1sxGG{bKc7V;{Xr>n-|ATcj)6K{cI;ZCo z&$8&@(M(HNOXl#UNTSMSV`|1-;9HpB5NtF&n(0W^cX%|@2*wSMW*W=D;n7TOtjO?a zrjOD$Jep}HeZ!-f&f*gZk7l}`357>9ac9H3>+TKw32LiG}D<393IWYH#2G- z&BTGAsC6_G4-1Q0M>Bnd(yXJIxXM1-`DiB28H`#-GjZ;6VH8I*sjf9Vnkm6>;n7UL zB-_o=Ov918q9~4LdV|#!9?ew4c;V4Z17pZaC0pi!F&)$AGGA%5F1*9a=HZ!?N$xJxDqBs@}uM3kUz|wi0otH zBK?wpS(6Yn8XQn`5jf8|m-m0gsfc?y)F_7|spF0C!^a<>PNS)6nvFh&C^(!5HEkv* z!pd1w>4y^?R^?w*R&nJc-$|8c-G|iv2jwH5UQ{@}D5-~C>4!!Evm6cNFERe0B4f%W z+kmS<%M4&22bBFFp#6ISQ05e%%qdJ=45;qkgL@9-nLW*Qt(Z-z4#`nRnV5N!ou)aa zPao5>q5d5X>(OmBmm_<^8IzqSc)ri4^z5zPLbf%~-N^fU)X$!r^0TIHjXHlSZR+H~ zBYtXYo9ixc%BVik2k@+FsBg6S)vK_FvgKdjT(c(4p=oVx1DtSzPbj43y_gxu@pOFJ zm0p0~0gkiT-vxZ6l*!bMOvT5B-y4n*GCsrMgVSkV;OF6H8-}IH)Qxl%E`_ir4&yN# z$2W{I))V1i?DRVhA3}ZwjWK?ix{>)T2Qtrcbf+8nVjSzujSz%2)=b^VYJ6x}50po0 z>V;on69-l&iMb79Xc1%TIa4>%3VFo{LwOiQ3*=?$MrK1Ef(FK5w@Px)J-CFjsuG z%T-Qzvvc#_>PpymP*=iwG=}wPw@vaNF`l600}MSvoy()afcd@_HE2=X<;-a|K5;JlZGakRB_PeF}NLm z5>B1P174lQ1FFvA0aa)5fU2{2K-F11pauC_zB-EsygG{qRGq~Gs?Op8RcG;ls)mc2C>MR~mbruh(I*SKXoy7yH z&f)cs3PXLPZ_n4G9#Xa? zjGAo;^Fmdct&ZaiK5HWp&fqJW5kjpwmW zXSgNgIO8c2_4ru^VJ*g@63$x+&W(BR&@W#}_M*|qd0@S_wAahD=X(txvHU#u2)a&o z*(cL51N!Wmqw8p`iZPq+(W7`j183ZW?&saL_wzB@ra5&QCcq?JUzwpW}YDJ;jB0{L}e|!<0A|v#fiiNF*k0X z|C`rv{4Mv1b!G+JfnQ(tFL2@v{{`{dtNxPBiSMZ0ymZ%wfn>Nd96ry=%ob`0{b=~A z$8hx&ZVbYhIIudD$NwN>j358;jGca5tHHO#7~?ku4)`<#rWg#d8z7{EPk8WeMEqys zz^d@qHd@3OzgggIc@KeMxhM~xx9}P>}_p%+rXIo#o=eHlZNOd>IX5vSC0?l)GSuSCjGLeondk3;sWZd(cF(&dAtEru9}HuR$)Dj3wcUIE7qoN2^z0PLo7SPFcU}wj{+&0&K0x*h zqYdhsX>W7VZ_PDOrESDVh$MbRo$ru7;zx}s<}?&RsZ%5%wgN% zRkrQ$mzq8Qs@l&8^*BX5av>z&FHk< zHpom`_>Jq={tw^p4X1~fm6I=r|KCk^`*xkv~cMglqYrmE56XBjWul-)w zkDst}H9@-c9m6xsPj(9&bzmV9d0 z#;I^SGP3hapu<06h5M+4n-GR$nB$X?okkYrVBTN5lYFYN-HE6x4|RoeET{CDg0fF+ z+p+PZ$n#i~4|I5GSU%?c_!P<_b-=O#?*soo)o}x4{ms3-!?DVM{wFL0^0*uIfU=?t z^V%;1-3{92%geN;+XlJ!9un3~Gu8|Rb+Z+6i;*X1 zgV&^FFK)-Tub>2Va4zJdj;}6BouKUBgWXB#JtI+1gWS}{orh!orIWg{gJmiO&$N$7 zUarc>vbkHCmkm3e;-@+mA}_=6j7#x+ebh}U>j`CI8(}=w5A(eZ;quXD*k1hO5C(0f z6m)OeN<`}67|8T1AZIF`4`m*K=R=vJ!{>ASAl?2-2I%&8*6~ z@$T|J(4ME7cW|H0F6X1B{{RogsrFd)!*hfP{c4?$a#DrXRbQi z0UPi^fziLx@A`^ye&^ze9uLO59NzeqRx@YPex;*%2oq6TYkJ?MHR@dog0*mmBVM%D zErF;!!mx<1ci{As^TFjWheW@G9!~62xN)X;FIs$)nl}wWFkkg?2JuVv-i1RdztL`u5Crp8Pet~ghIODfI{r@t^^T+q zL~;jC?E3l{%Vf)9o!4F5Q z5#G3XiiwUl<(S5Z6HKbd(62&svG@^8=V;9p#?{`B6E#;H??t~!n(H55ODU5zH!vP$ zZjaI2;P^M_H$`(vr}$_j!F~8*t~V`yE&Zm`iFL}&5WN}X)DE1l((_~%L+!x%pGJpN8G5rwh|~lxeJxH1HhQprUFVx$z`edk=^I z6Sx+8YQC!XWr@fgIK3?rk2`RBU(rk~em_Ot&4}EAa~rdLkLL6aoZh{6tHkvVoZfvp z-oW@)x<0_V;SQW1Zzd5R2`Q6qz* zMM6fBn#m*-u~t#=X{%MMwDt4Z3T?G&?bEONX=|xfv`%$wooa0z0Bfzb_O-9It*_tr z|F6CFIXjnx1O%s#{mZ%kz4oy7aP~Q8<*a?yGVM)H-N_o86iqx~L_gFdZC@|od`VOh59+NRF_JY$H$s0Tk_2q@v z5RPsL$sw$d`JP4A&tR<4bowt^+FkLB3;5b#xYhz^FkN#erEg>5PX!yL z?&S1nmYNlqThNsB3UZ#Ug{P;RnL9^wXQn^Rb22yBBz5Pe-(lesv~WfG2kdi2z;i+i zoZn>O_TU^Tyg1D(GguMuiWCc++*BBRHsHl37C5gZvsJ;`C@^SvmJ`Dh)|5D4m%HJe+)JS0rsoPtu^U04OeG+IGgJ)r;rvn=aL-Yo7txY zPPQ{BaTI=sJ%k_rA3QL?e$~Hmx{7_KksBpXVr+D%qBnOQljtV}UbR77UZAg$2$l zS*t2ISZKAt8B|NlVu4c(SJX&#vB1f##X+sou)ujA>(?m_3!J=Gf_kN4f%7%?X1UVP zyd^mZ_DQB~UtH`MD)y_g15q+8a31c`u)w(#^xFaUtIF5%tpRCHD?jH5+JY7#VS$qo z-nfK4EO2s37NL(y59f9CHkRtXyjF3VUcw~qJA|D)BVe0e@>cvtJ%I&IUOoO0Ufx(V z%p8>96|^5$FVc~WlouqOo_Uj0$8c@wy2v{f1^Sin?CgIJWFP-zG>ngcq#_b2{}>CL zd$A3_f)+UUl6&8x116#&f3Ff0^A{d)B8KJfB|J_#@H1e=aL@(#@#}bJf@MvLIDWkl z3%tw~C^HEh*5NSaDkN)(DR-g}CwfI$)EZfXSKaw)D{OljFj5=gw}F zS}azILRw&Bw)DhrqU?my@$)t|lHr^=*=$%iE?j-?>@~B>!Y93P;gs2NgVGEb&T`VE zCz3H?TzJUr&j^?CrNKb1o@u3v7JjO95^%xnSqnVij78ICR{(|F#^&g7&VUA9O|CCh z?z4~?^0r`U(m1YTObK~$zRad@vHy{f;P=ZTaNGdCw5mS^$4ne=f+=nMaIe4=(}2|F zrFIPtmL;Es$a~bu|3Ivg#^y+C8mijId;Fc?RoX4L-J2$@s;$LbwKvj!;saPH4iCF+ z_8(_01LQHVEe7ZWq&yy35_&2}QPI|6L6+mbIvZ?sDiUm*d9Ohk!zZu}Hjlb#C)rzz zaWsG@$3cnPj$=OJwhp4+BTz*CoAX??dDKn2{N5aC#zlUd56f>U;;b_P2SakO=Vc^j zeprtAXDFIf&nrQ>v6cX`978h>x81*>UB0JgyLcTPwR=9|wy%hK3v=4FdFLR{<(DXq z$FUdk>^Q!PxUGYz*VzNVE0E{%D*!*+KdaN_{9K2_<+l#}xUOuldAA{fajpl3EhzUh zI~kyv$K!5AfP`CMM|6@UfR^8bh-2-w*{t899F{^9Y`MLWV%I1$^C0{^7nO?nvAnZ9 zylWunEkh#M%FZ%&mE-4}xcuIQ!WFu}DA;zR1dX`qz3Z^u7TBIMQQ)_&&YCMf4xy}E zmmVN8aGc_cUY?)Zn&9@1{J8?-5JsMwOb}C-3_%+4r6>nF(Pw{cZUn#z(_$S4`EAsypwl`8St5~ntqIkCA z7ZtBkyk7Bk#Z8LxJ{o*~qx7E?|Dl+~+_2q!6c1LMt++su-+;4Rt0MQ(kiJFnPQ{-p z%8M}M%gZ$2aGodN!HQEAD-@S1HYuK^c#+~gijOP4p(y7ceEZ?jB>$m`#fr2a!Soju zw<`WcF~pi-{$Rx-#jN5nipMLSrr4zTImIt3{x8KF6z^1gK=B#HEsB3pOke>^{}o3m zeo}Fc;zGq5#dbw;Q-pE+htfAG{!sCkioa8QM=_u8F;Q=r;%LQk#c7K36{{566wg(> zOz~R9TNHny_=Mt1ihoor#5K(JrzoDFD0`)mzE0^sDf+nav%J_<#aiQDH(uwZntqhx z9O6Jc1C>5av6YDO9g1ga`UQ$t5>f9urN6KEoMJPt8`Q_G$qR6Mmir25OlJ>vSUVwY zcGzt-z1x&3pH!Za(EHx&PIEht_SMkmW)}NuxThuoyHaxtvG)RdYq+0;_N2TF_m0fp zOu8I-*q^-_mk~Z=VEHe-npK-oQWkr-1LP0m9`5Yg^swx-!LZLN`h1)UuK>2GynC+P z>J`GTrr|Gn-RkyrFSph0i-A_|@m8Jx9$Qm=w>8Dh?Xlgc1zeRl8;~opET{OH<(k@fvA^U zgP$+r=+6j5B2z1|hD|01+`%I7g!ye?Y&U8isgVouD|Vx9L8EFns*;>XmmsGg5mMX< zjom2P(N?=rcOgnon3I5^Yd0!9lmi0C6u+dgR5CRm_jXGaq>gs-L=PYK#ctFR)S@TM zHuNuy>_$z-q=k{)s8!4>H+@K^s@Tp%Llva`B zCT~RQ&&=y;H|i-gBc3qtWnN@A>R7fA+l`{tl`ys&buqd2VmHbM2l0d{GaK2BqABw5 zWYeKUYAG9y>_)LK;SSl2q5wrtm;pN!*^T--+gPq`KsCVy6^_+kbgz$T&-tg2vSTeF3^<`3#-6(Dq2_w5v z{n2cCi~3cCi~3{=~F5IrTMiiR?!Gku7ww8x=-&qcSWR*^N4jB_AF{;=ISMUM3^y>>lF4+IXTp*N=`k!E+l_h=Jm3ja1PSQj>Ebqw z?M8iug=4!>D2q!>+ntoYj)h~pQQRj4Pna}j7ZguPx3O?+H)yfWblb2SSUKNO_-r4c+~QavYn_)E#mu^aU;GaS27 z8Rl_(Nb zUyj|V)5*=T8jyitnb*3I*(m->_$D$`rES`bseUyuXdy0tup-(8;tEnQ6&yf znAI{h(8Dvro2@+Fp@(k=%~PQCa9&5^36s|M(^PzWMJF7J8KQ3H#{+#i1!v5!<9~g7}UT14l>&m)z;EJ|3 zAm8!yWWS&17q+A>^)Cq4hBF5ivMdAB`M=s29Ox#($ygbGOd7p1$ATSxTwXddDZ-aw zrm%EY(pJl_`GFI^+Q!<(mPV)~D~(G!=#$=~mr5|DVI}w5Mw#nMG-5p5VGHU(!5B4{NrU-5PSeQE#Y=v{yxPV{_Yz_PR>?#NBbvlph|Y@*Z!LvQeOm z?}D2t~DR2qDAibe?!p>6cN)FZfoWf=~|!SZ;LO6aK^+pu+5kmaaDu)#*BBEiO) zHxkb~J{fJWd6ci6WN!<`L3>ji2c)iO9P<&kbrAJd;hoMUPJ)ddk35&(yLi@eT;xaO z{1KPpV4Vp#7?Mz;7MiI1vK;es^}WMUZgNgJlFc~Wb`wwtWst^p`TEJWdp_c}uZVgJ z!LO7N4L0u_&@R7G;Ah8yDiOb2eeZe5%qTB{<#z?*F2Awh$MLd%cz0X&1PLPxNmygn^5jyTgl0m)z2Be<0 zR`E>W0ayT>ivWv(_l{omw&)Np1CORy#weC6@(V7e^FBu8eT8_MBF9L2t>VRsS1aD2 zc&FkfMZRrdJ$}hV+^YDNqS&}X`Y(gnCE$p^|IL9uZKT5Mba#l{tIndXa)E6`^vEjF$|^UXZ@h>a_t=<$L7&~&kJ z1)A^6Sx#(R0iRS_Y+QjB8&|-;X?njrQ(kObAzf@-0ma4@aH{5ujVsX0lolITpvA@& zP;6WQ#l{s-Y+M1w#uZR(Tmhfg`eNe>^gBu?v4Gj{;fiA83bfd`0?yQQv2g`jY+M1w z#uZR(Tmi+#6;Nzk0ma4@P;6WQ#l{s-Y+M1w#uZR(Tmi+#6;Nzk0ma4@P;6WQ#l{s- zY+M1?Q2z=P8&|+@DJ?dxK;Ngd*ti0{S!uCx1-cy9H0w`OJX=xpwn!HnSHS<)bl&Kx zH%%xWLPY)qr4Lt}rRhyv=O~X`h}gj5eTsBeX}SJE%l!jC7Qs@=;pW7560K!MncAz~ zby9h@&wAI(`7p*%*ayr!_9hv z@v{!T8RmWAcGTvmvDFb5An#4&{Xz3y zcJh4Wy^ini+5V$WdVutoQ0En`bGwrlA`jpD@Arb{UE|~>koOewexZ45oxD8cJ%YSP zHLuCZ>xaDikoQx~Tj=EFBX1+}?$*4?PTm0I{U7Ari9AfN$aP#_T#FeTB{;YqC*hca zVIAw*SOb{w8Ze}TYlv1yO%NF1`RhJJ!up>1TXOR z@5y~@H@=v*uOH_JoDAcE{+AH3`tc)iNvVGP9YmoY=Y}fi$4|j?%Ie4OL9OofKZUVGG!--bq2KhEcOclz^qRX@(piA6ttJL^UI@fmC(){l$AG}ez_My|c+$Me8J^y4zKk$!vv z8J}!An@D|)jYj%$_GQQP&H)IZmb{w65EaS z~#rkpCwGr#bUu37ZqaWXlLAd&H8vbzf<3resSU>&{ z$sYCN!&x`hk1yeT#`VbA*U^EfI;KiQ>&MrU+!_5iuT`rbKari>UHb7wn4w^B z5q^z+{16P-pBw4Nc_%db@mZoDUqt=*tXlNWU#R-=7cfTu-=zjiQ%W)}nAS=$=*Ks5 z79#!lt)K#rJHz(q$LmV#s;V2y4jWfKuDs0Z>iPDJ|9BbB=Lp7iou zSJ2DP9av~o^DN82bpAhe2o6LMT)jNzFjmWhkOCv{Yl>nGYp{Yn$=#xrHz$-j_+0h; z_LcJQQGaa~5kCd3tjAe{a=P|I^L6*msPo^OBHo+?uadh-yvpVkO*|dgM$p(%+u_)I z?wdZIFSO12+Hu3sJN~)%9?kq7%{;#8ULiiyj5D-78hQ09wQDr;FqMOwKV%Yl7t9Ai zIS%*1U>_H`I{7#Ef|{Vc9rAexp4XY0M`evB^xY1OIvi)KM0*X z-ZVSGmvcQTO7X?-*jk6?T6j6?w8^JDYJ%2xXq!Xa9Xfl?Jp=ndojs;K3d}3QVdZ7! zmqfYdyv6@@#439u;lvty3em3jDb5d-LF7dotC&^fd1pFx97JBf#3_o?6=y2WRjg24 zs7Rd%>n%}SrdX@kpxC6?rr4=?rsCO(s}(N+j=%!`w&K%@Z!7Y@dFGE&oT9iy@l3^a ziu~7>L%dMvfol;V!9MUwG7LNX_#Ha;L3mU2+jH!N!0d14UI5>GcRrw}@6PkG>tFdB^zPUx zf&B?D58-#`+x@OQ{**l7x)ICkNpFmc#dR8%f3Qb=vphpEg$N1Ew$bGid5`r!jUTHm z&LE!13wau0!~8)~`4+wj%1?%Gnne6U(-i*JBtA_lKfD!?%uoD_)C~V{K%Ug1{Nk7S zMF55h9R6wjR^lb%Sjk`L<+m{7=QslIHvaCK&yye?G`1}7=?kNs`##7lH+caXeexrd zne>JpoFZ|3Iz!xia+M-VwX!FtOU6jsOxm}2lg43{F@hw2) zjCyA*zwMG&4c)zGa95XIbGh#s*5eYIf{s*mA3zLtp)7{xfvlL_dP>T z<+3Pi>yQu2)iR>N=1m1{17Y+mkrh6>#HSfBiemR~jEwhp3R11{;YjA&pvhNU>%{w)SSJI}nQT7DgfyZqq% zrrZCgTXXm|IQ`>2%JRD~hu`t|2v4#E&{nw#ab&gGtU`3WFYga%0OBrN$>B%7=u#9G<78!!=)MQxymPE} zEweCf?36H~!S>J67Txssa~ak3;C$9rz}Vw%B;C z87gq_|9AYG83oqneZ>ah7C#Q~550Z9HFm5tiabP&W{zue1S*sh(QKdoheRd$9}+kQ z_2^qHtH|#!Nb|ZU&QP4IxIpn##VW-HMP5Iww^~vD$Hc<9j5P2bMPBcH@=XT+*)M1#@@V%keMgf>-=|E_oF`Y_ZW~$twx? zH+l!Yv2YjM+@oJxYx8JZ*;LFQb+vialc1XG-L|Wbp8nQ1?f?~Fe}y5K3)Bd(JfRG{ z`1}X(V+B~QGzhR*&n9|DV8qOnv3L zD-0rogd+U%Not?}wI(_GWgY|fdn9r6R&FfH*gA-c9Qy@EG}t^ot8AQEd@so>#Ri*) zn|2pjY&B$)8;~{x2Ok9-2m5tA4qFFNc^~`&BN}YpY|vm4g%aEpS)cs!aaev!5w~>^ z^=`n$oMA+Roj=}vU4B{cD?}RkG2QaxGuGwz5cnO4G#hN*=}2(-O>z21`N#5GgSf3j z9g61au5J6ZO6p9G^ut*e(#w{nX?%@T4J;GRYtu90%u}W3_7(QLh0B!x+(E`B~bco8G%D z>-L20xh;}rZ{o16-QoNkOTljItnZ#N(p@Fr-t7u=CgMQZ*2=Oxzm#R|w|Guad^JU#)nL;tt8A&22j%lOA!n`4p{pyPlJ~kx3IT zm%`pAr9EO6L4O|R#vTNk-Ts*0J18jSjegh8$dx;{k=)V zFJ;x&Od{n>$f~yhy2`5m!wi*GpJ3TMbN~jWiV$aDPbjO7_x$4|zaqb5f{lx`dYp); zODV0+jil9QQ0=27r(*f`ET_UG)A&Qj&#j$U7M?Y3e8sFD-qG@Uv4ImeePH5Jp3nVo znA6LDN+Nj`cEB0Bdp~Oz0Q0wzSKqtnCxiWHh`ki0A*0JploNK`v}s3WO7JRrOh&dD zXG-uMwXUk8ZcL_RT5E0Hg4Px7HFfQ)GRMxJmzj{wPCVnV((z-wV|h=l;_hU)J3}-N z*+(p#K5v%SFMzssT*s>B&Z=r)XS=}$8~d2btL_T3PgGv?T(9-xRyxLQ_ zTzQq{z91aTkL8yuuks$mE5!y|?kFUP5qGnv6WZbR_zvfmA(`ZqIam&|5IKKii{n4 z+6th30V|8{pf3MC$e{KGX@7k|`XtY;>8#u!D2Fh}-RXT3Iya$3BXrL48E0TmAavGx z{tU^6%!%kjOcjxHhRB)ou|J5fLX(Nd@!MbCit-ifN8zm~7GKZO=0l#J9h)r+`tfBb z{yppRF4UDVIZu!=XAVqW>c=wX(@2eE%r=>pZe>h+zEOO4$e1$3kDiRl347mV%#X)b zfNsAza95eRHR97utT~QW@+kR1^o#g)Aa47BsCO0kbtj+k$>8#{@+kT7K4trNA>uB- zJHRi4G#hN*8YH;d_ToHJbw(&f?s#?=mz9j-YmBn3!*!D^d{sXtBqwC zXn&)-@#e~-N21(ari%R9wutCb6l!s@3X#~`UVu&vp8uEHVEI|vqMP3P`%`-rnA*L+ zFXc*&1O2q|e~UP{t#jVEa^kQn_X6-<(cNAEBa+Gk#mc9+O;b!Yk2kJ8>ij>>@@jMI zXCn(?d$v1cX*NEQY#lQ`&B<$@@ffs?tcY8_9f{-98aXszlc0rNc$&rBd^YpY>`*zJj6mGug>XFUZvz3c(?QS zL%Vww*O@H7o&{E@@gsq?uktEo0CUfL8Zj%cvLTS&zZ7*viEUnrzQ2+t|3{X-Mk;@9 zL4D6JW_6zT>|SkKQQcD2T-V_`H?D&H`sGZjt)naCU9x+%`|o7kg=TJ#+sdq0;n|(U zLZz(2CnU>}{;0{U>oJZS&~J{TH<^`t?QO79L9Wb79k`trqMes&#BCk&=&Q`i{jhkn zOaCZ&Sbp8fti18tVB5--S(iEe+n&ryAD$V@3~2e~%BttUQ--a9iI!GV361 z%*w381bCn5>psp)Q4hEs%Wz?dF-?-P5Xq&BE`QOKUQ|7Y7MgM zz5por`HwVYSA}X&kX>&!iTI`L`W=(Fj#Pfw2I#8br!LLPuFtb9|Ecw7`iwKMCuTG1 zbjg;@sMGn@(8#XSjqFPG{w$5QVb`XVU7-6?y^qLiSFXpNrPxP3zqD-5@qO5X$~oH) zhdHA!A!g-Qv;b-+#8(QGto;6pv0bFszDu3&Sl!fs`K|KGQMOm+J9uEG^)Zjr`L z0&h9?txyFsoglX((7f?bUhA#gSd_7K5S4eR=QEg*hT36=NAFCPczh2va`!}bADZ!Iq73?mxs{PBf>+rI*p_sEaw zmY-;MbjemaEer;q-5NI{i&K{j+`Ns|dG$qn!S2PpAJX_;vRV_3Pkgd2<}} zXE})x4L0w3(8%HgN5UbPi0;Ol`wn#m%JoI3Kbp~Tg*{2~xmdY^(DRwBQM1=Vu?50L^vp6#*MNj|Vnj!mAR0ND-vT0WC5pg^{%mykE<|G|^GpHDf$+3x4dw5!llcnaF%-_W ze~-43%Rv4T5g8GlL%#>T1dVR%Wd3aA#76XY*2(-WG8FptvwK zk&S(?2)~?v<78g;ycds1aiHNHCkU5J>UEa9lkSv*;!!E%Wd3TQN>awj{H;Qb9SHN0 zUif3vLKak;7v639JPB00m+NHy4yiXKbrh%az6XSwHn6e@Tplo7rl&GUjg9F4lzn-` z^x8U^KMc>$pm<^G6>@p%Q5n%|6g2Z6#>r)PGI^)=C%i)m%@wF zyfR@#pI4;oWS$qBIGLC0dR6cX6c3AE%u7GajB|n+n8L95<-Bx;Jv%peN%G&!!ztiw z=ZOLL(C5>C*lLvh8;ZG3=9z}8Gd-WZ=`W`+z;s_ga)7VX4Q6a-P~u4Z4to$k6v|yE z^GqX~-?H8)-;n*%o7t(+{&SLB;3XO1A$N_yOQ@6if8qb4CmkpAd^C~Ue&lwt&R8P- z1j)q#O9#ct^wDH~O0W#6xKz_a(Iv--zB-v7DAQY!9>}^&f=i?)k(2p*rJ=~lydfhe z^M*V#O^r&pEJ(}LuJ+S>*#sl{qlLc6Pq&h{V?R#ep>Oun|HF(r zrEmAs=ke6nE4|TAtCM+C{(j#}auB>2W!m=j66$209f*?iz2rof-pAYLG|+F;$-Fot zXbnhnS}$gQ+Ja^w;bfi>-nfK4oXm4c@)RgNoY#@-Wd1jp#C_*+y3YvMrkA`0zfn(y zd;9U~@rUs8#-d^7paidx{kVF4dyo00q(6w?Bt3s~ZRxrQzl(}_&xnLVQ-FTI{hJ^G z`5Te!AMPIuV4xHP%lLE??T=?;{-P1}*t0plh#5;q(7LZbUTV%C0pkoH*pR>AfOfp- z@{b@Nzv4jub)r0g{e#%mcC?i#q8AH)sIc^rHiRlD|O9jiKe z;;>UzqRc-q@_sV%7LQ4=PUI0)$MJ|oR-~{DaITPo>z#! zWzzX}yz~FKm%G#<`IWL`&S=GKA`Bfb?osG?Pm13s5rEX=fw&u~1mR8im>($22 zELv%7f`L`^v5GCyz&yCtx3zZQgRUJnCbq}UygA7{V{p;l-c;9uO%RxDud-%EdwX3= zXJrkj<*n_=Z|tb7IkU=~*)DJS|Nr&?j~jt_ey{A%JHY#|d-xdd^|tRt_&|WXBq4XN z_tX2B_qaomi|4tNgH*81zL|@pcKpM#42R-id3o>wdn%Vjo~?tZl)K7Fu+gbGzd96rs`3(g>+dp(6 z;&&n9whoyb0)81rG}yc~pk00=oc;|&o*i%QUhm_;?{+&#lp{YL8x#Q&{s~3HBufBo zyEh?@wG!zm4jbGzJ;jMAcUL$3$kzx#Ci844AF@c~KeC)}c9qDn+BJ%(cQe+@!Hj6I z{j;=1H~szCH^a3N2V>{GB1&+3!|`-2y({*9bFLT`;-KDQU!lwo^D;TK7YTKv)D^o* zs76%vj0|GFhoJp8j)gX;d5!k^EhE+%_u~h^F^c7ipH$@ig8B0lPf=`8sgi=7tzpeO=BL9gbpCZLF#X7}i#m^~zL6I&wrG3TQ6z^5!fA!4& zwc^`~e^<=MVrTv^#nFnh6z3~0Q*2Q@Tk$^?`3(s9+^_gc#n%=0!NtP-(Tc|^o}hS| zBLBN*xmAi6D*i;i%M_1R zoTs=%@pQ$r6u+Q&mEw04?@;`y;?s(o75}UlVgXD46^j**Qk<>0K(Si!48<=iUafee z;tv#`SA1RZZ;Ji+x(DM;D~?h;OmV8>iHge9eVDosy zH+oOMlB_6vmHXccGwafL)a+O26>f&jn-I@-_#7Xy8R>b05YHA;?|9;73~j)a?AljD z??mATD?p;FB(H46+O4Cq_iWyny`TJGS7-fZrWbf?Ur%ONOFH{PejBq-zv5?xyo%E` zU?_E(nf1_V*1S3#y3b^0E!tYYxoygiU*|r&0R2Fjydb-N?%(2iM>S+Y$Mx1Dt~M&fPD+1f>EN zP2>eP0^lb63%KP%!&wBXUk7s%BlDM_a3V7kRQ`?R(!GChZ6ToKF>j4ne?K_Zb4A{@f^_Q6l2-+LVcSc!wGO>VQ!@egkJ zCsJoI>rg`_Q;n!#se)9-q5AG0oX=pYHrJyw;RHL5=h)6+w$&$?S8iJszt0m5Rgl_* z$qNrRRAK5|Qb!nSXzF@$nPjNpsRKzJX{Zq?d9!_#p)y|K7*zZXni19JerzxD4-NvV zO%@Q<=HJOC_76Usqwd8&_|H&RRGTuhk$><{lhetj3yD-M8AblV*_R#j4_*RpRBb-Y znvs8S`d$r}Ya4ih{6*v@Ze_!*MkX`2>4i5MBAMEo zEF=HmpX5^N!9Vy4cIA7f-tg3$q;7u{zX4R6qUenLgP+OSz0>HuMy1xXf02LiTiM=S z*P)%D*s3Ik;?h@mP{ z`?F_}fAHm`p1N1Yu-FTz*s=b>Sz!oAH-wIkhmgd4{~Npg0~G7(A3QEx>lEfSI+Qh_ zlfdub%kbmVCAHxzG384w=wQx?AF)EMuOK1$BmD3*_g{k_s5XD*()b6g1dq4#(IZi9 z{umMX2PYHiUrs>T$OCvHeXL6+jW#Lv4}KEs9<6l?)1Lqj$3OT8);(5pv*~3l9Qz0V z1t)U4=1xktF*o)PzL2@GfAG;fBeS*e^z=DwDE1Hj8y1fJgWt)*Cure{H2)WH{DW7s zaO@wPSElP9d?Hyp{=t`V5RQNFzcBxtfP$r{HbwP$Zt!%Hw5T>Y+j(UiPAFRGVJlFU zg0!eMQAxv_unS>$|LaECP1kj8`G^$&gp$=E;mNo2Jw7$8#%)#fMJ563^asK2YF z<*<0YpRQnAj(_kqWasz?=MxU9O*S4DL$!G{BaVOYd8D`FA3Ru!0z2X#{739?>>r%g z?x5PN#(08as5TkljY}?5s5aX{7oiEIhpz)Is!d+2)<5_vPRh>t2QL9{ET+f7c2Hs? zev@19!?mS1ir`KZ@cSh?(5YYoGxk3aVvm16+V@AwA|(sTGf-{T<4cE(XM4OK0*RC6^~cebvqu^JhxL&G<#wdIq%%DU#d_T_agHBik& z>amqI?VYX7)wasYnhv2mG$H;Jq^km_sk+|y%x#+pbs7{wZoBZ~?69PUucNLeN5R)c zCup4TR>OdzQ3y7`ej=x#-m7eFsiY#W(r5!~T3b(VWTB4bO)KgQOa3R|I#7g)u8NvN z)jmPH2%^2N1J0?jd$6+Rvz?+#Y^|^7W@Pl8BgVMx6m->UnNfl{WwQ`*32b5>sDaYDzu6FMFkt?@M z-wvP;M(f$q*jnA8)|I-RMfSR?$>>(QC|b>QVG!st#+0LWHQFnA2P)&NTvZ2`NIRiS zZK*r66X#ge#;urJvnr9$QQKJC*wRQPailJud&0CimGfszTQq;ctdnL`F62qAs*UH^ zsF$6w%IP8RRidplZsIE|P498qskSu!<86|o&BUz;OQ;3D-K|~|XSIsg7*1eaRSUNI zRW?_hsf!DGPj4mK6LvMNE4VN!KU>8$inU*dWf$qWt?t=$SGCpA6<=B1bh=q`GAWH* zaCLR)1*eLO4{P=G%BCu#KUZzGxS+3GG`AHl!@Bx_w|$RwvMX9vbkx;W@|IwA<5)GV zE5KYXDXwp}+H`n%m01{ABV$*0>J^>Xi_q6V{TQC{@-C2ds1tCX1NP~VtLt2cmx&pa<-0}JXE{7%w$XJ` zf8+*B9*RVkmuDcbr*c{3**b_yxfUnEMyKYK zF&}YT2T|`SyzRQuNwCpeUFXITW?baQ@!5H)M%>mx)T_n&*|Cghu=7kQ)$QN?;K%o~ zfJJ1qNx!iBEo<+I7=sG>TSmwVVz3DnD zuwIVsrd=mVq8ytIAC#{19ovB}XO{EGl@y0vsq1_fY^m$yJ2)G-en(mYNV+tK=6~)s zFyAU8YCP42#?LZ7yRmH#kMriL(_t@#kNOVtSZ{1@ThU%u$$gs$O*p70-9p4Vc$n%* zOA$uBg%%&oC^t%J@xcsQd@uvsG+lf!gBBmmK=Hv0yg~ED2Qz4F2o}CSSKO-j2gP?4 zQ-LWbKA2HYd@uv~s)79$AI!iSrO#00R~^j%uA=y01}#3Af#QQ1C_b2h;)59|KA3^x zgBd73n1Rc1Lm)r#!3-21%)r|&8f#QQ1_@t(b4`$G> zD=j{lLBp-A)Ds`fphqcvnBr7L@xhGzWlFayiVtR_iw|a?_+SQ#4`!hFU)+@^1d*q7`W}x_B28s`6p!i@0iVtR> z_+SQ#4`!hFU_+SQhsNPe2FoV8QY4O1f`ZlG-2Q%nj zC@nsiK_~D)<#@{#rzwgLW~5)F^j5_;72j19y(!jS3J);qJ4X{y?=xK2p!tmLi=OoV E0gsN{S^xk5 diff --git a/ports/cortex_a9/gnu/example_build/libgcc.a b/ports/cortex_a9/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 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 z 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%0D}YP&K(hZs1Db1;{G{zL_9zMUiJsM-Q$jk3iWRR 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≪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 O)*~DyMuzP+-v0+$^S6Nj diff --git a/ports/cortex_m3/gnu/inc/tx_port.h b/ports/cortex_m3/gnu/inc/tx_port.h index 9ab8886f..af6bade2 100644 --- a/ports/cortex_m3/gnu/inc/tx_port.h +++ b/ports/cortex_m3/gnu/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M3/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m3/gnu/src/tx_thread_schedule.S b/ports/cortex_m3/gnu/src/tx_thread_schedule.S index 7c2d6c3e..e5b425fb 100644 --- a/ports/cortex_m3/gnu/src/tx_thread_schedule.S +++ b/ports/cortex_m3/gnu/src/tx_thread_schedule.S @@ -37,7 +37,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M3/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -64,13 +64,14 @@ /* */ /* _tx_initialize_kernel_enter ThreadX entry function */ /* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -91,7 +92,7 @@ _tx_thread_schedule: /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit MSR CONTROL, r0 // Setup new CONTROL register @@ -151,7 +152,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -213,7 +214,7 @@ __tx_ts_restore: LDR r12, [r1, #8] // Pickup thread's stack pointer LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers @@ -271,7 +272,7 @@ __tx_ts_ready: B __tx_ts_restore // Restore the thread // } -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP .global tx_thread_fpu_enable .thumb_func diff --git a/ports/cortex_m3/gnu/src/tx_timer_interrupt.S b/ports/cortex_m3/gnu/src/tx_timer_interrupt.S index 2edd6db1..5c3d6a2e 100644 --- a/ports/cortex_m3/gnu/src/tx_timer_interrupt.S +++ b/ports/cortex_m3/gnu/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M3/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports/cortex_m3/iar/inc/tx_port.h b/ports/cortex_m3/iar/inc/tx_port.h index ae4d092e..33b8a2bb 100644 --- a/ports/cortex_m3/iar/inc/tx_port.h +++ b/ports/cortex_m3/iar/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M3/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m3/iar/src/tx_timer_interrupt.s b/ports/cortex_m3/iar/src/tx_timer_interrupt.s index 13427464..29ee9a3a 100644 --- a/ports/cortex_m3/iar/src/tx_timer_interrupt.s +++ b/ports/cortex_m3/iar/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M3/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER PUBLIC _tx_timer_interrupt _tx_timer_interrupt: @@ -249,4 +253,5 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif END diff --git a/ports/cortex_m3/keil/inc/tx_port.h b/ports/cortex_m3/keil/inc/tx_port.h index 5cc5fe6a..369f912b 100644 --- a/ports/cortex_m3/keil/inc/tx_port.h +++ b/ports/cortex_m3/keil/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M3/Keil */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m33/ac6/inc/tx_port.h b/ports/cortex_m33/ac6/inc/tx_port.h index ef91eaea..26620c2c 100644 --- a/ports/cortex_m33/ac6/inc/tx_port.h +++ b/ports/cortex_m33/ac6/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h Cortex-M33/AC6 */ -/* 6.1.9 */ +/* tx_port.h Cortex-M33 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -43,6 +43,9 @@ /* own special types that can be mapped to actual data types by this */ /* file to guarantee consistency in the interface and functionality. */ /* */ +/* This file replaces the previous Cortex-M33 files. It unifies */ +/* the Cortex-M33 compilers into one common file. */ +/* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ @@ -51,14 +54,20 @@ /* 03-02-2021 Scott Larson Modified comment(s), added */ /* ULONG64_DEFINED, */ /* resulting in version 6.1.5 */ -/* 06-02-2021 Yuxin Zhou Modified comment(s), removed */ -/* unneeded header file, */ +/* 06-02-2021 Scott Larson Modified comment(s), removed */ +/* unneeded header file, funcs */ +/* set_control and get_control */ +/* changed to inline, */ /* added symbol to enable */ /* stack error handler, */ /* resulting in version 6.1.7 */ /* 10-15-2021 Scott Larson Modified comment(s), improved */ /* stack check error handling, */ /* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), unified */ +/* this file across compilers, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -68,17 +77,32 @@ /* Determine if the optional ThreadX user define file should be used. */ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" -#endif +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ /* Define compiler library include files. */ #include #include + +#ifdef __ICCARM__ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm from each compiler look similar */ +#define _tx_control_get __get_CONTROL +#define _tx_control_set __set_CONTROL +#define _tx_ipsr_get __get_IPSR +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ +#endif /* __ICCARM__ */ + +#ifdef __ARMCOMPILER_VERSION #include +#endif + /* Define ThreadX basic types for this port. */ @@ -101,21 +125,17 @@ UINT _txe_thread_secure_stack_free(struct TX_THREAD_STRUCT *thread_ptr); UINT _tx_thread_secure_stack_allocate(struct TX_THREAD_STRUCT *tx_thread, ULONG stack_size); UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); -/* This port handles stack errors. */ -#define TX_PORT_THREAD_STACK_ERROR_HANDLING - - -/* Define the system API mappings based on the error checking - selected by the user. Note: this section is only applicable to +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to application source code, hence the conditional that turns off this stuff when the include file is processed by the ThreadX source. */ #ifndef TX_SOURCE_CODE -/* Determine if error checking is desired. If so, map API functions +/* Determine if error checking is desired. If so, map API functions to the appropriate error checking front-ends. Otherwise, map API - functions to the core functions that actually perform the work. + functions to the core functions that actually perform the work. Note: error checking is enabled by default. */ #ifdef TX_DISABLE_ERROR_CHECKING @@ -132,10 +152,11 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define tx_thread_secure_stack_allocate _txe_thread_secure_stack_allocate #define tx_thread_secure_stack_free _txe_thread_secure_stack_free -#endif -#endif - +#endif /* TX_DISABLE_ERROR_CHECKING */ +#endif /* TX_SOURCE_CODE */ +/* This port has a usage fault handler in _tx_initialize_low_level for stack exceptions. */ +#define TX_PORT_THREAD_STACK_ERROR_HANDLING /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ @@ -160,19 +181,19 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY +#ifndef TX_TIMER_THREAD_PRIORITY #define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX Cortex-M33 port. */ +/* Define various constants for the ThreadX Cortex-M port. */ #define TX_INT_DISABLE 1 /* Disable interrupts */ #define TX_INT_ENABLE 0 /* Enable interrupts */ /* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock source constants would be: #define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) @@ -211,7 +232,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #endif -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -225,16 +246,35 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ #define TX_THREAD_EXTENSION_0 #define TX_THREAD_EXTENSION_1 + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +/* IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_secure_stack_context; +/* ThreadX in non-secure zone with calls to secure zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_secure_stack_context; \ + VOID *tx_thread_iar_tls_pointer; #else +/* ThreadX in only one zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_iar_tls_pointer; +#endif + +#else +/* No IAR library support */ +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) +/* ThreadX in non-secure zone with calls to secure zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_secure_stack_context; +#else +/* ThreadX in only one zone. */ #define TX_THREAD_EXTENSION_2 #endif + +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + #define TX_THREAD_EXTENSION_3 @@ -249,7 +289,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION @@ -259,38 +299,39 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, tx_thread_shell_entry, and tx_thread_terminate. */ - - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ + if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} +#else +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#endif +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); + +#else /* No IAR library support. */ +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* Define the size of the secure stack for the timer thread and use the extension to allocate the secure stack. */ -#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 +#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 #define TX_TIMER_INITIALIZE_EXTENSION(status) _tx_thread_secure_stack_allocate(&_tx_timer_thread, TX_TIMER_THREAD_SECURE_STACK_SIZE); #endif -#ifndef TX_MISRA_ENABLE - -//register unsigned int _ipsr __asm ("MRS %[result], ipsr" : [result] "=r" (_ipsr) : ); -inline static unsigned int _get_ipsr(void); -inline static unsigned int _get_ipsr(void) -{ - unsigned int _ipsr; - __asm("MRS %[result], ipsr" : [result] "=r" (_ipsr) : ); - return _ipsr; -} - -#endif - - -#ifdef __ARM_PCS_VFP +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -299,128 +340,128 @@ void _tx_misra_control_set(ULONG value); ULONG _tx_misra_fpccr_get(void); void _tx_misra_vfp_touch(void); -#else +#else /* TX_MISRA_ENABLE not defined */ -#ifdef TX_SOURCE_CODE +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ -static unsigned int _get_control(void); -static unsigned int _get_control(void) +__attribute__( ( always_inline ) ) static inline ULONG _tx_control_get(void) { - unsigned int _control; - __asm("MRS %[result], control" : [result] "=r" (_control) : ); - return _control; +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); } -static void _set_control(unsigned int _control); -static void _set_control(unsigned int _control) +__attribute__( ( always_inline ) ) static inline void _tx_control_set(ULONG control_value) { - __asm("MSR control, %[input]" : : [input] "r" (_control)); + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); } -#endif -#endif +#endif /* __GNUC__ */ + +/* Touch VFP register in order to flush. Works for AC6/GCC/IAR compilers. */ +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); + +#endif /* TX_MISRA_ENABLE */ /* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA in order to ensure no lazy stacking will occur. */ #ifndef TX_MISRA_ENABLE -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state);; \ - } +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } #else -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } - +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } #endif /* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating - this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush the lazy FPU save, then restore the CONTROL.FPCA state. */ #ifndef TX_MISRA_ENABLE -void _tx_vfp_access(void); - -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = *((ULONG *) 0xE000EF34); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_vfp_access(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + TX_VFP_TOUCH(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #else -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = _tx_misra_fpccr_get(); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_misra_vfp_touch(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #endif -#else +#else /* No VFP in use */ #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -445,16 +486,27 @@ void _tx_vfp_access(void); #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Define the get system state macro. */ - +/* Define the get system state macro. */ + #ifndef TX_THREAD_GET_SYSTEM_STATE #ifndef TX_MISRA_ENABLE -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _get_ipsr()) -#else + +#if defined(__GNUC__) /* GCC and AC6 */ +__attribute__( ( always_inline ) ) static inline UINT _tx_ipsr_get(void) +{ +UINT ipsr_value; + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} +#endif /* GCC and AC6 IPSR_get function. */ + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_ipsr_get()) + +#else /* TX_MISRA_ENABLE is defined, use MISRA function. */ ULONG _tx_misra_ipsr_get(VOID); #define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) -#endif -#endif +#endif /* TX_MISRA_ENABLE */ +#endif /* TX_THREAD_GET_SYSTEM_STATE */ /* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value @@ -472,31 +524,102 @@ extern void _tx_thread_secure_stack_initialize(void); #define TX_INITIALIZE_KERNEL_ENTER_EXTENSION _tx_thread_secure_stack_initialize(); #endif -/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to prevent early scheduling on Cortex-M parts. */ - + #define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the - lowest bit set. */ + #ifndef TX_DISABLE_INLINE -#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __clz(__rbit((m))); - +/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */ +#ifdef __ICCARM__ /* IAR Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); +#elif defined(__GNUC__) /* GCC and AC6 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#else +#error "Compiler not supported." #endif -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ -#ifdef TX_DISABLE_INLINE +/* Define the interrupt disable/restore macros. */ + +__attribute__( ( always_inline ) ) static inline UINT __get_interrupt_posture(void) +{ +UINT posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(UINT basepri_value) +{ + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(UINT int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif +} + +__attribute__( ( always_inline ) ) static inline UINT __disable_interrupts(void) +{ +UINT int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); +} + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +UINT interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (_tx_ipsr_get() == 0) + { + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif + __restore_interrupt(interrupt_save); + } +} + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/* Redefine _tx_thread_system_return for improved performance. */ +#define _tx_thread_system_return _tx_thread_system_return_inline + +#else /* TX_DISABLE_INLINE is defined */ UINT _tx_thread_interrupt_disable(VOID); VOID _tx_thread_interrupt_restore(UINT previous_posture); @@ -504,41 +627,14 @@ VOID _tx_thread_interrupt_restore(UIN #define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); - #define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); - -#else - -#define TX_INTERRUPT_SAVE_AREA UINT was_masked; -#define TX_DISABLE was_masked = __disable_irq(); -#define TX_RESTORE if (was_masked == 0) __enable_irq(); - -#define _tx_thread_system_return _tx_thread_system_return_inline - - -static void _tx_thread_system_return_inline(void) -{ -unsigned int was_masked; - - - /* Set PendSV to invoke ThreadX scheduler. */ - *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (_get_ipsr() == 0) - { - was_masked = __disable_irq(); - __enable_irq(); - if (was_masked != 0) - __disable_irq(); - } -} -#endif - +#endif /* TX_DISABLE_INLINE */ /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT -CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33/AC6 Version 6.1.9 *"; +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33 Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports/cortex_m33/ac6/inc/tx_secure_interface.h b/ports/cortex_m33/ac6/inc/tx_secure_interface.h index bab64f05..d6fba106 100644 --- a/ports/cortex_m33/ac6/inc/tx_secure_interface.h +++ b/ports/cortex_m33/ac6/inc/tx_secure_interface.h @@ -42,7 +42,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ diff --git a/ports/cortex_m33/ac6/src/tx_thread_secure_stack.c b/ports/cortex_m33/ac6/src/tx_thread_secure_stack.c index af8d77f0..d41bdbbe 100644 --- a/ports/cortex_m33/ac6/src/tx_thread_secure_stack.c +++ b/ports/cortex_m33/ac6/src/tx_thread_secure_stack.c @@ -23,7 +23,7 @@ #include "tx_api.h" -/* If TX_SINGLE_MODE_SECURE or TX_SINGLE_MODE_NON_SECURE is defined, +/* If TX_SINGLE_MODE_SECURE or TX_SINGLE_MODE_NON_SECURE is defined, no secure stack functionality is needed. */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) @@ -45,8 +45,14 @@ #define TX_THREAD_STACK_SEAL_SIZE 8 #define TX_THREAD_STACK_SEAL_VALUE 0xFEF5EDA5 -/* Secure stack info struct to hold stack start, stack limit, - current stack pointer, and pointer to owning thread. +/* max number of Secure context */ +#ifndef TX_MAX_SECURE_CONTEXTS +#define TX_MAX_SECURE_CONTEXTS 32 +#endif +#define TX_INVALID_SECURE_CONTEXT_IDX (-1) + +/* Secure stack info struct to hold stack start, stack limit, + current stack pointer, and pointer to owning thread. This will be allocated for each thread with a secure stack. */ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT { @@ -54,8 +60,14 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT VOID *tx_thread_secure_stack_start; /* Thread's secure stack start address */ VOID *tx_thread_secure_stack_limit; /* Thread's secure stack limit */ TX_THREAD *tx_thread_ptr; /* Keep track of thread for error handling */ + INT tx_next_free_index; /* Next free index of free secure context */ } TX_THREAD_SECURE_STACK_INFO; +/* Static secure contexts */ +static TX_THREAD_SECURE_STACK_INFO tx_thread_secure_context[TX_MAX_SECURE_CONTEXTS]; +/* Head of free secure context */ +static INT tx_head_free_index = 0U; + /**************************************************************************/ @@ -63,7 +75,7 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_initialize Cortex-M33/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -102,12 +114,16 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT /* changed name, execute in */ /* handler mode, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) UINT _tx_thread_secure_mode_stack_initialize(void) { UINT status; +INT index; /* Make sure function is called from interrupt (threads should not call). */ if (__get_IPSR() == 0) @@ -118,12 +134,26 @@ UINT status; { /* Set secure mode to use PSP. */ __set_CONTROL(__get_CONTROL() | 2); - + /* Set process stack pointer and stack limit to 0 to throw exception when a thread without a secure stack calls a secure function that tries to use secure stack. */ __set_PSPLIM(0); __set_PSP(0); - + + for (index = 0; index < TX_MAX_SECURE_CONTEXTS; index++) + { + + /* Check last index and mark next free to invalid index */ + if(index == (TX_MAX_SECURE_CONTEXTS - 1)) + { + tx_thread_secure_context[index].tx_next_free_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + else + { + tx_thread_secure_context[index].tx_next_free_index = index + 1; + } + } + status = TX_SUCCESS; } return status; @@ -136,7 +166,7 @@ UINT status; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_allocate Cortex-M33/AC6 */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -160,9 +190,7 @@ UINT status; /* CALLS */ /* */ /* __get_IPSR Intrinsic to get IPSR */ -/* calloc Compiler's calloc function */ /* malloc Compiler's malloc function */ -/* free Compiler's free() function */ /* __set_PSPLIM Intrinsic to set PSP limit */ /* __set_PSP Intrinsic to set PSP */ /* __TZ_get_PSPLIM_NS Intrinsic to get NS PSP */ @@ -179,17 +207,22 @@ UINT status; /* 10-16-2020 Scott Larson Modified comment(s), */ /* added stack sealing, */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) UINT _tx_thread_secure_mode_stack_allocate(TX_THREAD *thread_ptr, ULONG stack_size) { +TX_INTERRUPT_SAVE_AREA UINT status; TX_THREAD_SECURE_STACK_INFO *info_ptr; UCHAR *stack_mem; +INT secure_context_index; status = TX_SUCCESS; - + /* Make sure function is called from interrupt (threads should not call). */ if (__get_IPSR() == 0) { @@ -199,23 +232,39 @@ UCHAR *stack_mem; { status = TX_SIZE_ERROR; } - + /* Check if thread already has secure stack allocated. */ else if (thread_ptr -> tx_thread_secure_stack_context != 0) { status = TX_THREAD_ERROR; } - + else { - /* Allocate space for secure stack info. */ - info_ptr = calloc(1, sizeof(TX_THREAD_SECURE_STACK_INFO)); - - if(info_ptr != TX_NULL) + + TX_DISABLE + + /* Allocate free index for secure stack info. */ + if(tx_head_free_index != TX_INVALID_SECURE_CONTEXT_IDX) { + secure_context_index = tx_head_free_index; + tx_head_free_index = tx_thread_secure_context[tx_head_free_index].tx_next_free_index; + tx_thread_secure_context[secure_context_index].tx_next_free_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + else + { + secure_context_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + + TX_RESTORE + + if(secure_context_index != TX_INVALID_SECURE_CONTEXT_IDX) + { + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* If stack info allocated, allocate a stack & seal. */ stack_mem = malloc(stack_size + TX_THREAD_STACK_SEAL_SIZE); - + if(stack_mem != TX_NULL) { /* Secure stack has been allocated, save in the stack info struct. */ @@ -223,13 +272,13 @@ UCHAR *stack_mem; info_ptr -> tx_thread_secure_stack_start = stack_mem + stack_size; info_ptr -> tx_thread_secure_stack_ptr = info_ptr -> tx_thread_secure_stack_start; info_ptr -> tx_thread_ptr = thread_ptr; - + /* Seal bottom of stack. */ *(ULONG*)info_ptr -> tx_thread_secure_stack_start = TX_THREAD_STACK_SEAL_VALUE; - - /* Save info pointer in thread. */ - thread_ptr -> tx_thread_secure_stack_context = info_ptr; - + + /* Save secure context id (i.e non-zero base index) in thread. */ + thread_ptr -> tx_thread_secure_stack_context = (VOID *)(secure_context_index + 1); + /* Check if this thread is running by looking at its stack start and PSPLIM_NS */ if(((ULONG) thread_ptr -> tx_thread_stack_start & 0xFFFFFFF8) == __TZ_get_PSPLIM_NS()) { @@ -238,21 +287,26 @@ UCHAR *stack_mem; __set_PSP((ULONG)(info_ptr -> tx_thread_secure_stack_ptr)); } } - + else { + TX_DISABLE + /* Stack not allocated, free the info struct. */ - free(info_ptr); + tx_thread_secure_context[secure_context_index].tx_next_free_index = tx_head_free_index; + tx_head_free_index = secure_context_index; + TX_RESTORE + status = TX_NO_MEMORY; } } - + else { status = TX_NO_MEMORY; } } - + return(status); } @@ -263,7 +317,7 @@ UCHAR *stack_mem; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_free Cortex-M33/AC6 */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -298,44 +352,65 @@ UCHAR *stack_mem; /* 09-30-2020 Scott Larson Initial Version 6.1 */ /* 10-16-2020 Scott Larson Modified comment(s), */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr) { +TX_INTERRUPT_SAVE_AREA UINT status; TX_THREAD_SECURE_STACK_INFO *info_ptr; +INT secure_context_index; status = TX_SUCCESS; - - /* Pickup stack info from thread. */ - info_ptr = thread_ptr -> tx_thread_secure_stack_context; - + + /* Pickup stack info id from thread. */ + secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; + /* Make sure function is called from interrupt (threads should not call). */ if (__get_IPSR() == 0) { status = TX_CALLER_ERROR; } - - /* Check that this secure context is for this thread. */ - else if (info_ptr -> tx_thread_ptr != thread_ptr) + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) { status = TX_THREAD_ERROR; } - else { - - /* Free secure stack. */ - free(info_ptr -> tx_thread_secure_stack_limit); - - /* Free info struct. */ - free(info_ptr); - - /* Clear secure context from thread. */ - thread_ptr -> tx_thread_secure_stack_context = 0; + + /* Pickup stack info from static array of secure contexts. */ + info_ptr = &tx_thread_secure_context[secure_context_index]; + + /* Check that this secure context is for this thread. */ + if (info_ptr -> tx_thread_ptr != thread_ptr) + { + status = TX_THREAD_ERROR; + } + + else + { + + /* Free secure stack. */ + free(info_ptr -> tx_thread_secure_stack_limit); + + TX_DISABLE + + /* Free info struct. */ + tx_thread_secure_context[secure_context_index].tx_next_free_index = tx_head_free_index; + tx_head_free_index = secure_context_index; + TX_RESTORE + + /* Clear secure context from thread. */ + thread_ptr -> tx_thread_secure_stack_context = 0; + } } - + return(status); } @@ -346,7 +421,7 @@ TX_THREAD_SECURE_STACK_INFO *info_ptr; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_stack_context_save Cortex-M33/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -383,6 +458,9 @@ TX_THREAD_SECURE_STACK_INFO *info_ptr; /* resulting in version 6.1.1 */ /* 06-02-2021 Scott Larson Fix stack pointer save, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -390,38 +468,45 @@ void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr) { TX_THREAD_SECURE_STACK_INFO *info_ptr; ULONG sp; +INT secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; /* This function should be called from scheduler only. */ if (__get_IPSR() == 0) { return; } - + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) + { + return; + } + /* Pickup the secure context pointer. */ - info_ptr = (TX_THREAD_SECURE_STACK_INFO *)(thread_ptr -> tx_thread_secure_stack_context); - + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* Check that this secure context is for this thread. */ if (info_ptr -> tx_thread_ptr != thread_ptr) { return; } - + /* Check that stack pointer is in range */ sp = __get_PSP(); - if ((sp < (ULONG)info_ptr -> tx_thread_secure_stack_limit) || + if ((sp < (ULONG)info_ptr -> tx_thread_secure_stack_limit) || (sp > (ULONG)info_ptr -> tx_thread_secure_stack_start)) { return; } - + /* Save stack pointer. */ info_ptr -> tx_thread_secure_stack_ptr = (VOID *) sp; - + /* Set process stack pointer and stack limit to 0 to throw exception when a thread without a secure stack calls a secure function that tries to use secure stack. */ __set_PSPLIM(0); __set_PSP(0); - + return; } @@ -432,7 +517,7 @@ ULONG sp; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_stack_context_restore Cortex-M33/AC6 */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -466,32 +551,42 @@ ULONG sp; /* 09-30-2020 Scott Larson Initial Version 6.1 */ /* 10-16-2020 Scott Larson Modified comment(s), */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr) { TX_THREAD_SECURE_STACK_INFO *info_ptr; +INT secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; /* This function should be called from scheduler only. */ if (__get_IPSR() == 0) { return; } - + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) + { + return; + } + /* Pickup the secure context pointer. */ - info_ptr = (TX_THREAD_SECURE_STACK_INFO *)(thread_ptr -> tx_thread_secure_stack_context); - + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* Check that this secure context is for this thread. */ if (info_ptr -> tx_thread_ptr != thread_ptr) { return; } - + /* Set stack pointer and limit. */ __set_PSPLIM((ULONG)info_ptr -> tx_thread_secure_stack_limit); __set_PSP ((ULONG)info_ptr -> tx_thread_secure_stack_ptr); - + return; } diff --git a/ports/cortex_m33/gnu/inc/tx_port.h b/ports/cortex_m33/gnu/inc/tx_port.h index a56ca7c0..26620c2c 100644 --- a/ports/cortex_m33/gnu/inc/tx_port.h +++ b/ports/cortex_m33/gnu/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h Cortex-M33/GNU */ -/* 6.1.9 */ +/* tx_port.h Cortex-M33 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -43,6 +43,9 @@ /* own special types that can be mapped to actual data types by this */ /* file to guarantee consistency in the interface and functionality. */ /* */ +/* This file replaces the previous Cortex-M33 files. It unifies */ +/* the Cortex-M33 compilers into one common file. */ +/* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ @@ -51,15 +54,20 @@ /* 03-02-2021 Scott Larson Modified comment(s), added */ /* ULONG64_DEFINED, */ /* resulting in version 6.1.5 */ -/* 06-02-2021 Scott Larson Modified comment(s), changed */ +/* 06-02-2021 Scott Larson Modified comment(s), removed */ +/* unneeded header file, funcs */ /* set_control and get_control */ -/* to be inline functions, */ +/* changed to inline, */ /* added symbol to enable */ /* stack error handler, */ /* resulting in version 6.1.7 */ /* 10-15-2021 Scott Larson Modified comment(s), improved */ /* stack check error handling, */ /* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), unified */ +/* this file across compilers, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -69,17 +77,33 @@ /* Determine if the optional ThreadX user define file should be used. */ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" -#endif +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ /* Define compiler library include files. */ #include #include +#ifdef __ICCARM__ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm from each compiler look similar */ +#define _tx_control_get __get_CONTROL +#define _tx_control_set __set_CONTROL +#define _tx_ipsr_get __get_IPSR +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ +#endif /* __ICCARM__ */ + +#ifdef __ARMCOMPILER_VERSION +#include +#endif + + /* Define ThreadX basic types for this port. */ #define VOID void @@ -101,21 +125,17 @@ UINT _txe_thread_secure_stack_free(struct TX_THREAD_STRUCT *thread_ptr); UINT _tx_thread_secure_stack_allocate(struct TX_THREAD_STRUCT *tx_thread, ULONG stack_size); UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); -/* This port handles stack errors. */ -#define TX_PORT_THREAD_STACK_ERROR_HANDLING - - -/* Define the system API mappings based on the error checking - selected by the user. Note: this section is only applicable to +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to application source code, hence the conditional that turns off this stuff when the include file is processed by the ThreadX source. */ #ifndef TX_SOURCE_CODE -/* Determine if error checking is desired. If so, map API functions +/* Determine if error checking is desired. If so, map API functions to the appropriate error checking front-ends. Otherwise, map API - functions to the core functions that actually perform the work. + functions to the core functions that actually perform the work. Note: error checking is enabled by default. */ #ifdef TX_DISABLE_ERROR_CHECKING @@ -132,10 +152,11 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define tx_thread_secure_stack_allocate _txe_thread_secure_stack_allocate #define tx_thread_secure_stack_free _txe_thread_secure_stack_free -#endif -#endif - +#endif /* TX_DISABLE_ERROR_CHECKING */ +#endif /* TX_SOURCE_CODE */ +/* This port has a usage fault handler in _tx_initialize_low_level for stack exceptions. */ +#define TX_PORT_THREAD_STACK_ERROR_HANDLING /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ @@ -160,19 +181,19 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY +#ifndef TX_TIMER_THREAD_PRIORITY #define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX Cortex-M33 port. */ +/* Define various constants for the ThreadX Cortex-M port. */ #define TX_INT_DISABLE 1 /* Disable interrupts */ #define TX_INT_ENABLE 0 /* Enable interrupts */ /* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock source constants would be: #define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) @@ -211,7 +232,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #endif -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -225,16 +246,35 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ #define TX_THREAD_EXTENSION_0 #define TX_THREAD_EXTENSION_1 + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +/* IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_secure_stack_context; +/* ThreadX in non-secure zone with calls to secure zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_secure_stack_context; \ + VOID *tx_thread_iar_tls_pointer; #else +/* ThreadX in only one zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_iar_tls_pointer; +#endif + +#else +/* No IAR library support */ +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) +/* ThreadX in non-secure zone with calls to secure zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_secure_stack_context; +#else +/* ThreadX in only one zone. */ #define TX_THREAD_EXTENSION_2 #endif + +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + #define TX_THREAD_EXTENSION_3 @@ -249,7 +289,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION @@ -259,38 +299,39 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, tx_thread_shell_entry, and tx_thread_terminate. */ - - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ + if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} +#else +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#endif +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); + +#else /* No IAR library support. */ +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* Define the size of the secure stack for the timer thread and use the extension to allocate the secure stack. */ -#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 +#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 #define TX_TIMER_INITIALIZE_EXTENSION(status) _tx_thread_secure_stack_allocate(&_tx_timer_thread, TX_TIMER_THREAD_SECURE_STACK_SIZE); #endif -#ifndef TX_MISRA_ENABLE - -//register unsigned int _ipsr __asm ("MRS %[result], ipsr" : [result] "=r" (_ipsr) : ); -inline static unsigned int _get_ipsr(void); -inline static unsigned int _get_ipsr(void) -{ - unsigned int _ipsr; - __asm("MRS %[result], ipsr" : [result] "=r" (_ipsr) : ); - return _ipsr; -} - -#endif - - -#ifdef __ARM_PCS_VFP +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -299,128 +340,128 @@ void _tx_misra_control_set(ULONG value); ULONG _tx_misra_fpccr_get(void); void _tx_misra_vfp_touch(void); -#else +#else /* TX_MISRA_ENABLE not defined */ -#ifdef TX_SOURCE_CODE +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ -static inline unsigned int _get_control(void); -static inline unsigned int _get_control(void) +__attribute__( ( always_inline ) ) static inline ULONG _tx_control_get(void) { - unsigned int _control; - __asm("MRS %[result], control" : [result] "=r" (_control) : ); - return _control; +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); } -static inline void _set_control(unsigned int _control); -static inline void _set_control(unsigned int _control) +__attribute__( ( always_inline ) ) static inline void _tx_control_set(ULONG control_value) { - __asm("MSR control, %[input]" : : [input] "r" (_control)); + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); } -#endif -#endif +#endif /* __GNUC__ */ + +/* Touch VFP register in order to flush. Works for AC6/GCC/IAR compilers. */ +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); + +#endif /* TX_MISRA_ENABLE */ /* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA in order to ensure no lazy stacking will occur. */ #ifndef TX_MISRA_ENABLE -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state);; \ - } +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } #else -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } - +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } #endif /* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating - this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush the lazy FPU save, then restore the CONTROL.FPCA state. */ #ifndef TX_MISRA_ENABLE -void _tx_vfp_access(void); - -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = *((ULONG *) 0xE000EF34); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_vfp_access(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + TX_VFP_TOUCH(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #else -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = _tx_misra_fpccr_get(); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_misra_vfp_touch(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #endif -#else +#else /* No VFP in use */ #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -445,16 +486,27 @@ void _tx_vfp_access(void); #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Define the get system state macro. */ - +/* Define the get system state macro. */ + #ifndef TX_THREAD_GET_SYSTEM_STATE #ifndef TX_MISRA_ENABLE -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _get_ipsr()) -#else + +#if defined(__GNUC__) /* GCC and AC6 */ +__attribute__( ( always_inline ) ) static inline UINT _tx_ipsr_get(void) +{ +UINT ipsr_value; + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} +#endif /* GCC and AC6 IPSR_get function. */ + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_ipsr_get()) + +#else /* TX_MISRA_ENABLE is defined, use MISRA function. */ ULONG _tx_misra_ipsr_get(VOID); #define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) -#endif -#endif +#endif /* TX_MISRA_ENABLE */ +#endif /* TX_THREAD_GET_SYSTEM_STATE */ /* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value @@ -472,106 +524,117 @@ extern void _tx_thread_secure_stack_initialize(void); #define TX_INITIALIZE_KERNEL_ENTER_EXTENSION _tx_thread_secure_stack_initialize(); #endif -/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to prevent early scheduling on Cortex-M parts. */ - + #define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the - lowest bit set. */ + #ifndef TX_DISABLE_INLINE - -#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ - __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */ +#ifdef __ICCARM__ /* IAR Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); +#elif defined(__GNUC__) /* GCC and AC6 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#else +#error "Compiler not supported." #endif -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ -#ifndef TX_DISABLE_INLINE +/* Define the interrupt disable/restore macros. */ -/* Define GNU specific macros, with in-line assembly for performance. */ - -__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +__attribute__( ( always_inline ) ) static inline UINT __get_interrupt_posture(void) { - -unsigned int primask_value; - - __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); - __asm__ volatile (" CPSID i" : : : "memory" ); - return(primask_value); +UINT posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); } -__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int primask_value) +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(UINT basepri_value) { - - __asm__ volatile (" MSR PRIMASK,%0": : "r" (primask_value): "memory" ); + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); } - -__attribute__( ( always_inline ) ) static inline unsigned int __get_primask_value(void) -{ - -unsigned int primask_value; - - __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); - return(primask_value); -} - +#else __attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) { + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif - __asm__ volatile (" CPSIE i": : : "memory" ); +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(UINT int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif } +__attribute__( ( always_inline ) ) static inline UINT __disable_interrupts(void) +{ +UINT int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); +} __attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) { -unsigned int interrupt_save; +UINT interrupt_save; + /* Set PendSV to invoke ThreadX scheduler. */ *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (_get_ipsr() == 0) + if (_tx_ipsr_get() == 0) { - interrupt_save = __get_primask_value(); + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else __enable_interrupts(); - __restore_interrupts(interrupt_save); +#endif + __restore_interrupt(interrupt_save); } } - #define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; - #define TX_DISABLE interrupt_save = __disable_interrupts(); -#define TX_RESTORE __restore_interrupts(interrupt_save); - +#define TX_RESTORE __restore_interrupt(interrupt_save); /* Redefine _tx_thread_system_return for improved performance. */ - #define _tx_thread_system_return _tx_thread_system_return_inline +#else /* TX_DISABLE_INLINE is defined */ -#else +UINT _tx_thread_interrupt_disable(VOID); +VOID _tx_thread_interrupt_restore(UINT previous_posture); -#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; - -#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); -#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); -#endif +#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif /* TX_DISABLE_INLINE */ /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT -CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33/GNU Version 6.1.9 *"; +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33 Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports/cortex_m33/gnu/inc/tx_secure_interface.h b/ports/cortex_m33/gnu/inc/tx_secure_interface.h index bab64f05..d6fba106 100644 --- a/ports/cortex_m33/gnu/inc/tx_secure_interface.h +++ b/ports/cortex_m33/gnu/inc/tx_secure_interface.h @@ -42,7 +42,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ diff --git a/ports/cortex_m33/gnu/src/tx_initialize_low_level.S b/ports/cortex_m33/gnu/src/tx_initialize_low_level.S index 97a26765..b3910b01 100644 --- a/ports/cortex_m33/gnu/src/tx_initialize_low_level.S +++ b/ports/cortex_m33/gnu/src/tx_initialize_low_level.S @@ -34,7 +34,7 @@ HEAP_SIZE = 0x00000000 /* FUNCTION RELEASE */ /* */ /* _tx_initialize_low_level Cortex-M33/GNU */ -/* 6.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -68,6 +68,8 @@ HEAP_SIZE = 0x00000000 /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_initialize_low_level(VOID) @@ -218,7 +220,7 @@ _unhandled_usage_loop: // Handle stack overflow STR r1, [r0] // Clear CFSR flag(s) -#ifdef __ARM_PCS_VFP +#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 diff --git a/ports/cortex_m33/gnu/src/tx_thread_schedule.S b/ports/cortex_m33/gnu/src/tx_thread_schedule.S index 9f3adbbf..caf48c51 100644 --- a/ports/cortex_m33/gnu/src/tx_thread_schedule.S +++ b/ports/cortex_m33/gnu/src/tx_thread_schedule.S @@ -26,7 +26,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M33/GNU */ -/* 6.1.6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -65,6 +65,8 @@ /* 06-02-2021 Scott Larson Added secure stack initialize */ /* in SVC handler, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -86,7 +88,7 @@ _tx_thread_schedule: LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag STR r0, [r2, #0] // Clear preempt disable flag -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit @@ -145,7 +147,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -230,7 +232,7 @@ _skip_secure_restore: MSR PSPLIM, r12 // Set stack limit LDR r12, [r1, #8] // Pickup thread's stack pointer LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers diff --git a/ports/cortex_m33/gnu/src/tx_thread_secure_stack.c b/ports/cortex_m33/gnu/src/tx_thread_secure_stack.c index 38ddfbf8..0b064098 100644 --- a/ports/cortex_m33/gnu/src/tx_thread_secure_stack.c +++ b/ports/cortex_m33/gnu/src/tx_thread_secure_stack.c @@ -23,7 +23,7 @@ #include "tx_api.h" -/* If TX_SINGLE_MODE_SECURE or TX_SINGLE_MODE_NON_SECURE is defined, +/* If TX_SINGLE_MODE_SECURE or TX_SINGLE_MODE_NON_SECURE is defined, no secure stack functionality is needed. */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) @@ -44,8 +44,14 @@ #define TX_THREAD_STACK_SEAL_SIZE 8 #define TX_THREAD_STACK_SEAL_VALUE 0xFEF5EDA5 -/* Secure stack info struct to hold stack start, stack limit, - current stack pointer, and pointer to owning thread. +/* max number of Secure context */ +#ifndef TX_MAX_SECURE_CONTEXTS +#define TX_MAX_SECURE_CONTEXTS 32 +#endif +#define TX_INVALID_SECURE_CONTEXT_IDX (-1) + +/* Secure stack info struct to hold stack start, stack limit, + current stack pointer, and pointer to owning thread. This will be allocated for each thread with a secure stack. */ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT { @@ -53,8 +59,14 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT VOID *tx_thread_secure_stack_start; /* Thread's secure stack start address */ VOID *tx_thread_secure_stack_limit; /* Thread's secure stack limit */ TX_THREAD *tx_thread_ptr; /* Keep track of thread for error handling */ + INT tx_next_free_index; /* Next free index of free secure context */ } TX_THREAD_SECURE_STACK_INFO; +/* Static secure contexts */ +static TX_THREAD_SECURE_STACK_INFO tx_thread_secure_context[TX_MAX_SECURE_CONTEXTS]; +/* Head of free secure context */ +static INT tx_head_free_index = 0U; + /**************************************************************************/ @@ -62,7 +74,7 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_initialize Cortex-M33/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -98,6 +110,9 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT /* handler mode, */ /* disable optimizations, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry, optimize(0))) @@ -106,6 +121,7 @@ UINT _tx_thread_secure_mode_stack_initialize(void) UINT status; ULONG control; ULONG ipsr; +INT index; /* Make sure function is called from interrupt (threads should not call). */ asm volatile("MRS %0, IPSR" : "=r" (ipsr)); /* Get IPSR register. */ @@ -119,12 +135,26 @@ ULONG ipsr; asm volatile("MRS %0, CONTROL" : "=r" (control)); /* Get CONTROL register. */ control |= 2; /* Use PSP. */ asm volatile("MSR CONTROL, %0" :: "r" (control)); /* Set CONTROL register. */ - + /* Set process stack pointer and stack limit to 0 to throw exception when a thread without a secure stack calls a secure function that tries to use secure stack. */ asm volatile("MSR PSPLIM, %0" :: "r" (0)); asm volatile("MSR PSP, %0" :: "r" (0)); - + + for (index = 0; index < TX_MAX_SECURE_CONTEXTS; index++) + { + + /* Check last index and mark next free to invalid index */ + if(index == (TX_MAX_SECURE_CONTEXTS - 1)) + { + tx_thread_secure_context[index].tx_next_free_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + else + { + tx_thread_secure_context[index].tx_next_free_index = index + 1; + } + } + status = TX_SUCCESS; } return status; @@ -137,7 +167,7 @@ ULONG ipsr; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_allocate Cortex-M33/GNU */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -160,9 +190,7 @@ ULONG ipsr; /* */ /* CALLS */ /* */ -/* calloc Compiler's calloc function */ /* malloc Compiler's malloc function */ -/* free Compiler's free() function */ /* */ /* CALLED BY */ /* */ @@ -176,6 +204,9 @@ ULONG ipsr; /* 10-16-2020 Scott Larson Modified comment(s), */ /* added stack sealing, */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -186,9 +217,10 @@ TX_THREAD_SECURE_STACK_INFO *info_ptr; UCHAR *stack_mem; ULONG ipsr; ULONG psplim_ns; +INT secure_context_index; status = TX_SUCCESS; - + /* Make sure function is called from interrupt (threads should not call). */ asm volatile("MRS %0, IPSR" : "=r" (ipsr)); /* Get IPSR register. */ if (ipsr == 0) @@ -199,23 +231,38 @@ ULONG psplim_ns; { status = TX_SIZE_ERROR; } - + /* Check if thread already has secure stack allocated. */ else if (thread_ptr -> tx_thread_secure_stack_context != 0) { status = TX_THREAD_ERROR; } - + else { - /* Allocate space for secure stack info. */ - info_ptr = calloc(1, sizeof(TX_THREAD_SECURE_STACK_INFO)); - - if(info_ptr != TX_NULL) + TX_DISABLE + + /* Allocate free index for secure stack info. */ + if(tx_head_free_index != TX_INVALID_SECURE_CONTEXT_IDX) { + secure_context_index = tx_head_free_index; + tx_head_free_index = tx_thread_secure_context[tx_head_free_index].tx_next_free_index; + tx_thread_secure_context[secure_context_index].tx_next_free_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + else + { + secure_context_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + + TX_RESTORE + + if(secure_context_index != TX_INVALID_SECURE_CONTEXT_IDX) + { + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* If stack info allocated, allocate a stack & seal. */ stack_mem = malloc(stack_size + TX_THREAD_STACK_SEAL_SIZE); - + if(stack_mem != TX_NULL) { /* Secure stack has been allocated, save in the stack info struct. */ @@ -223,13 +270,13 @@ ULONG psplim_ns; info_ptr -> tx_thread_secure_stack_start = stack_mem + stack_size; info_ptr -> tx_thread_secure_stack_ptr = info_ptr -> tx_thread_secure_stack_start; info_ptr -> tx_thread_ptr = thread_ptr; - + /* Seal bottom of stack. */ *(ULONG*)info_ptr -> tx_thread_secure_stack_start = TX_THREAD_STACK_SEAL_VALUE; - - /* Save info pointer in thread. */ - thread_ptr -> tx_thread_secure_stack_context = info_ptr; - + + /* Save secure context id (i.e non-zero base index) in thread. */ + thread_ptr -> tx_thread_secure_stack_context = (VOID *)(secure_context_index + 1); + /* Check if this thread is running by looking at its stack start and PSPLIM_NS */ asm volatile("MRS %0, PSPLIM_NS" : "=r" (psplim_ns)); /* Get PSPLIM_NS register. */ if(((ULONG) thread_ptr -> tx_thread_stack_start & 0xFFFFFFF8) == psplim_ns) @@ -239,21 +286,26 @@ ULONG psplim_ns; asm volatile("MSR PSP, %0" :: "r" ((ULONG)(info_ptr -> tx_thread_secure_stack_ptr))); } } - + else { + TX_DISABLE + /* Stack not allocated, free the info struct. */ - free(info_ptr); + tx_thread_secure_context[secure_context_index].tx_next_free_index = tx_head_free_index; + tx_head_free_index = secure_context_index; + TX_RESTORE + status = TX_NO_MEMORY; } } - + else { status = TX_NO_MEMORY; } } - + return(status); } @@ -264,7 +316,7 @@ ULONG psplim_ns; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_free Cortex-M33/GNU */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -298,46 +350,67 @@ ULONG psplim_ns; /* 09-30-2020 Scott Larson Initial Version 6.1 */ /* 10-16-2020 Scott Larson Modified comment(s), */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr) { +TX_INTERRUPT_SAVE_AREA UINT status; TX_THREAD_SECURE_STACK_INFO *info_ptr; ULONG ipsr; +INT secure_context_index; status = TX_SUCCESS; - - /* Pickup stack info from thread. */ - info_ptr = thread_ptr -> tx_thread_secure_stack_context; - + + /* Pickup stack info id from thread. */ + secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; + /* Make sure function is called from interrupt (threads should not call). */ asm volatile("MRS %0, IPSR" : "=r" (ipsr)); /* Get IPSR register. */ if (ipsr == 0) { status = TX_CALLER_ERROR; } - - /* Check that this secure context is for this thread. */ - else if (info_ptr -> tx_thread_ptr != thread_ptr) + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) { status = TX_THREAD_ERROR; } - else { - - /* Free secure stack. */ - free(info_ptr -> tx_thread_secure_stack_limit); - - /* Free info struct. */ - free(info_ptr); - - /* Clear secure context from thread. */ - thread_ptr -> tx_thread_secure_stack_context = 0; + + /* Pickup stack info from static array of secure contexts. */ + info_ptr = &tx_thread_secure_context[secure_context_index]; + + /* Check that this secure context is for this thread. */ + if (info_ptr -> tx_thread_ptr != thread_ptr) + { + status = TX_THREAD_ERROR; + } + + else + { + + /* Free secure stack. */ + free(info_ptr -> tx_thread_secure_stack_limit); + + TX_DISABLE + + /* Free info struct. */ + tx_thread_secure_context[secure_context_index].tx_next_free_index = tx_head_free_index; + tx_head_free_index = secure_context_index; + TX_RESTORE + + /* Clear secure context from thread. */ + thread_ptr -> tx_thread_secure_stack_context = 0; + } } - + return(status); } @@ -348,7 +421,7 @@ ULONG ipsr; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_stack_context_save Cortex-M33/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -382,6 +455,9 @@ ULONG ipsr; /* resulting in version 6.1.1 */ /* 06-02-2021 Scott Larson Fix stack pointer save, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -390,6 +466,7 @@ void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr) TX_THREAD_SECURE_STACK_INFO *info_ptr; ULONG sp; ULONG ipsr; +INT secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; /* This function should be called from scheduler only. */ asm volatile("MRS %0, IPSR" : "=r" (ipsr)); /* Get IPSR register. */ @@ -397,32 +474,38 @@ ULONG ipsr; { return; } - + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) + { + return; + } + /* Pickup the secure context pointer. */ - info_ptr = (TX_THREAD_SECURE_STACK_INFO *)(thread_ptr -> tx_thread_secure_stack_context); - + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* Check that this secure context is for this thread. */ if (info_ptr -> tx_thread_ptr != thread_ptr) { return; } - + /* Check that stack pointer is in range */ asm volatile("MRS %0, PSP" : "=r" (sp)); /* Get PSP register. */ - if ((sp < (ULONG)info_ptr -> tx_thread_secure_stack_limit) || + if ((sp < (ULONG)info_ptr -> tx_thread_secure_stack_limit) || (sp > (ULONG)info_ptr -> tx_thread_secure_stack_start)) { return; } - + /* Save stack pointer. */ info_ptr -> tx_thread_secure_stack_ptr = (VOID *) sp; - + /* Set process stack pointer and stack limit to 0 to throw exception when a thread without a secure stack calls a secure function that tries to use secure stack. */ asm volatile("MSR PSPLIM, %0" :: "r" (0)); asm volatile("MSR PSP, %0" :: "r" (0)); - + return; } @@ -433,7 +516,7 @@ ULONG ipsr; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_stack_context_restore Cortex-M33/GNU */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -465,6 +548,9 @@ ULONG ipsr; /* 09-30-2020 Scott Larson Initial Version 6.1 */ /* 10-16-2020 Scott Larson Modified comment(s), */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -472,6 +558,7 @@ void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr) { TX_THREAD_SECURE_STACK_INFO *info_ptr; ULONG ipsr; +INT secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; /* This function should be called from scheduler only. */ asm volatile("MRS %0, IPSR" : "=r" (ipsr)); /* Get IPSR register. */ @@ -479,20 +566,26 @@ ULONG ipsr; { return; } - + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) + { + return; + } + /* Pickup the secure context pointer. */ - info_ptr = (TX_THREAD_SECURE_STACK_INFO *)(thread_ptr -> tx_thread_secure_stack_context); - + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* Check that this secure context is for this thread. */ if (info_ptr -> tx_thread_ptr != thread_ptr) { return; } - + /* Set stack pointer and limit. */ asm volatile("MSR PSPLIM, %0" :: "r" ((ULONG)info_ptr -> tx_thread_secure_stack_limit)); asm volatile("MSR PSP, %0" :: "r" ((ULONG)info_ptr -> tx_thread_secure_stack_ptr)); - + return; } diff --git a/ports/cortex_m33/iar/inc/tx_port.h b/ports/cortex_m33/iar/inc/tx_port.h index 42b16f8b..26620c2c 100644 --- a/ports/cortex_m33/iar/inc/tx_port.h +++ b/ports/cortex_m33/iar/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h Cortex-M33/IAR */ -/* 6.1.9 */ +/* tx_port.h Cortex-M33 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -43,6 +43,9 @@ /* own special types that can be mapped to actual data types by this */ /* file to guarantee consistency in the interface and functionality. */ /* */ +/* This file replaces the previous Cortex-M33 files. It unifies */ +/* the Cortex-M33 compilers into one common file. */ +/* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ @@ -51,13 +54,20 @@ /* 03-02-2021 Scott Larson Modified comment(s), added */ /* ULONG64_DEFINED, */ /* resulting in version 6.1.5 */ -/* 06-02-2021 Yuxin Zhou Modified comment(s), and */ +/* 06-02-2021 Scott Larson Modified comment(s), removed */ +/* unneeded header file, funcs */ +/* set_control and get_control */ +/* changed to inline, */ /* added symbol to enable */ /* stack error handler, */ /* resulting in version 6.1.7 */ /* 10-15-2021 Scott Larson Modified comment(s), improved */ /* stack check error handling, */ /* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), unified */ +/* this file across compilers, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -67,19 +77,30 @@ /* Determine if the optional ThreadX user define file should be used. */ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" -#endif +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ /* Define compiler library include files. */ #include #include -#include + +#ifdef __ICCARM__ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm from each compiler look similar */ +#define _tx_control_get __get_CONTROL +#define _tx_control_set __set_CONTROL +#define _tx_ipsr_get __get_IPSR #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT #include +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ +#endif /* __ICCARM__ */ + +#ifdef __ARMCOMPILER_VERSION +#include #endif @@ -104,21 +125,17 @@ UINT _txe_thread_secure_stack_free(struct TX_THREAD_STRUCT *thread_ptr); UINT _tx_thread_secure_stack_allocate(struct TX_THREAD_STRUCT *tx_thread, ULONG stack_size); UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); -/* This port handles stack errors. */ -#define TX_PORT_THREAD_STACK_ERROR_HANDLING - - -/* Define the system API mappings based on the error checking - selected by the user. Note: this section is only applicable to +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to application source code, hence the conditional that turns off this stuff when the include file is processed by the ThreadX source. */ #ifndef TX_SOURCE_CODE -/* Determine if error checking is desired. If so, map API functions +/* Determine if error checking is desired. If so, map API functions to the appropriate error checking front-ends. Otherwise, map API - functions to the core functions that actually perform the work. + functions to the core functions that actually perform the work. Note: error checking is enabled by default. */ #ifdef TX_DISABLE_ERROR_CHECKING @@ -135,10 +152,11 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define tx_thread_secure_stack_allocate _txe_thread_secure_stack_allocate #define tx_thread_secure_stack_free _txe_thread_secure_stack_free -#endif -#endif - +#endif /* TX_DISABLE_ERROR_CHECKING */ +#endif /* TX_SOURCE_CODE */ +/* This port has a usage fault handler in _tx_initialize_low_level for stack exceptions. */ +#define TX_PORT_THREAD_STACK_ERROR_HANDLING /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ @@ -163,19 +181,19 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY +#ifndef TX_TIMER_THREAD_PRIORITY #define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX Cortex-M33 port. */ +/* Define various constants for the ThreadX Cortex-M port. */ #define TX_INT_DISABLE 1 /* Disable interrupts */ #define TX_INT_ENABLE 0 /* Enable interrupts */ /* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock source constants would be: #define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) @@ -214,7 +232,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #endif -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -228,7 +246,7 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ #define TX_THREAD_EXTENSION_0 @@ -255,10 +273,11 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_THREAD_EXTENSION_2 #endif -#endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #define TX_THREAD_EXTENSION_3 + /* Define the port extensions of the remaining ThreadX objects. */ #define TX_BLOCK_POOL_EXTENSION @@ -270,7 +289,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION @@ -284,34 +303,35 @@ ULONG _tx_misra_time_stamp_get(VOID); void *_tx_iar_create_per_thread_tls_area(void); void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); void __iar_Initlocks(void); -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) #define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ - thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ - thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); #endif #define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); -#else -/* No IAR library support. */ -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) + +#else /* No IAR library support. */ +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) #define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* Define the size of the secure stack for the timer thread and use the extension to allocate the secure stack. */ -#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 +#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 #define TX_TIMER_INITIALIZE_EXTENSION(status) _tx_thread_secure_stack_allocate(&_tx_timer_thread, TX_TIMER_THREAD_SECURE_STACK_SIZE); #endif -#ifdef __ARMVFP__ + +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -320,107 +340,128 @@ void _tx_misra_control_set(ULONG value); ULONG _tx_misra_fpccr_get(void); void _tx_misra_vfp_touch(void); -#endif +#else /* TX_MISRA_ENABLE not defined */ + +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline ULONG _tx_control_get(void) +{ +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); +} + +__attribute__( ( always_inline ) ) static inline void _tx_control_set(ULONG control_value) +{ + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); +} + +#endif /* __GNUC__ */ + +/* Touch VFP register in order to flush. Works for AC6/GCC/IAR compilers. */ +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); + +#endif /* TX_MISRA_ENABLE */ /* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA in order to ensure no lazy stacking will occur. */ #ifndef TX_MISRA_ENABLE -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ - } +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } #else -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } - +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } #endif /* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating - this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush the lazy FPU save, then restore the CONTROL.FPCA state. */ #ifndef TX_MISRA_ENABLE -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = *((ULONG *) 0xE000EF34); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - __asm volatile ("vmov.f32 s0, s0"); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + TX_VFP_TOUCH(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #else -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = _tx_misra_fpccr_get(); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_misra_vfp_touch(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #endif -#else +#else /* No VFP in use */ #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -445,16 +486,27 @@ void _tx_misra_vfp_touch(void); #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Define the get system state macro. */ - +/* Define the get system state macro. */ + #ifndef TX_THREAD_GET_SYSTEM_STATE #ifndef TX_MISRA_ENABLE -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) -#else + +#if defined(__GNUC__) /* GCC and AC6 */ +__attribute__( ( always_inline ) ) static inline UINT _tx_ipsr_get(void) +{ +UINT ipsr_value; + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} +#endif /* GCC and AC6 IPSR_get function. */ + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_ipsr_get()) + +#else /* TX_MISRA_ENABLE is defined, use MISRA function. */ ULONG _tx_misra_ipsr_get(VOID); #define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) -#endif -#endif +#endif /* TX_MISRA_ENABLE */ +#endif /* TX_THREAD_GET_SYSTEM_STATE */ /* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value @@ -472,31 +524,102 @@ extern void _tx_thread_secure_stack_initialize(void); #define TX_INITIALIZE_KERNEL_ENTER_EXTENSION _tx_thread_secure_stack_initialize(); #endif -/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to prevent early scheduling on Cortex-M parts. */ - + #define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the - lowest bit set. */ + #ifndef TX_DISABLE_INLINE -#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT)__CLZ(__RBIT((m))); - +/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */ +#ifdef __ICCARM__ /* IAR Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); +#elif defined(__GNUC__) /* GCC and AC6 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#else +#error "Compiler not supported." #endif -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ -#ifdef TX_DISABLE_INLINE +/* Define the interrupt disable/restore macros. */ + +__attribute__( ( always_inline ) ) static inline UINT __get_interrupt_posture(void) +{ +UINT posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(UINT basepri_value) +{ + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(UINT int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif +} + +__attribute__( ( always_inline ) ) static inline UINT __disable_interrupts(void) +{ +UINT int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); +} + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +UINT interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (_tx_ipsr_get() == 0) + { + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif + __restore_interrupt(interrupt_save); + } +} + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/* Redefine _tx_thread_system_return for improved performance. */ +#define _tx_thread_system_return _tx_thread_system_return_inline + +#else /* TX_DISABLE_INLINE is defined */ UINT _tx_thread_interrupt_disable(VOID); VOID _tx_thread_interrupt_restore(UINT previous_posture); @@ -504,49 +627,14 @@ VOID _tx_thread_interrupt_restore(UIN #define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); - #define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); - -#else - -#define TX_INTERRUPT_SAVE_AREA __istate_t interrupt_save; -#define TX_DISABLE {interrupt_save = __get_interrupt_state();__disable_interrupt();}; -#define TX_RESTORE {__set_interrupt_state(interrupt_save);}; - -#define _tx_thread_system_return _tx_thread_system_return_inline - -static void _tx_thread_system_return_inline(void) -{ -__istate_t interrupt_save; - - /* Set PendSV to invoke ThreadX scheduler. */ - *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (__get_IPSR() == 0) - { - interrupt_save = __get_interrupt_state(); - __enable_interrupt(); - __set_interrupt_state(interrupt_save); - } -} - -#endif - - -/* Define the interrupt lockout macros for each ThreadX object. */ - -#define TX_BLOCK_POOL_DISABLE TX_DISABLE -#define TX_BYTE_POOL_DISABLE TX_DISABLE -#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE -#define TX_MUTEX_DISABLE TX_DISABLE -#define TX_QUEUE_DISABLE TX_DISABLE -#define TX_SEMAPHORE_DISABLE TX_DISABLE - +#endif /* TX_DISABLE_INLINE */ /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT -CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33/IAR Version 6.1.9 *"; +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33 Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports/cortex_m33/iar/inc/tx_secure_interface.h b/ports/cortex_m33/iar/inc/tx_secure_interface.h index bab64f05..d6fba106 100644 --- a/ports/cortex_m33/iar/inc/tx_secure_interface.h +++ b/ports/cortex_m33/iar/inc/tx_secure_interface.h @@ -42,7 +42,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ /* */ /**************************************************************************/ diff --git a/ports/cortex_m33/iar/src/tx_thread_secure_stack.c b/ports/cortex_m33/iar/src/tx_thread_secure_stack.c index f48ff16d..94d41db7 100644 --- a/ports/cortex_m33/iar/src/tx_thread_secure_stack.c +++ b/ports/cortex_m33/iar/src/tx_thread_secure_stack.c @@ -23,7 +23,7 @@ #include "tx_api.h" -/* If TX_SINGLE_MODE_SECURE or TX_SINGLE_MODE_NON_SECURE is defined, +/* If TX_SINGLE_MODE_SECURE or TX_SINGLE_MODE_NON_SECURE is defined, no secure stack functionality is needed. */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) @@ -45,8 +45,14 @@ #define TX_THREAD_STACK_SEAL_SIZE 8 #define TX_THREAD_STACK_SEAL_VALUE 0xFEF5EDA5 -/* Secure stack info struct to hold stack start, stack limit, - current stack pointer, and pointer to owning thread. +/* max number of Secure context */ +#ifndef TX_MAX_SECURE_CONTEXTS +#define TX_MAX_SECURE_CONTEXTS 32 +#endif +#define TX_INVALID_SECURE_CONTEXT_IDX (-1) + +/* Secure stack info struct to hold stack start, stack limit, + current stack pointer, and pointer to owning thread. This will be allocated for each thread with a secure stack. */ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT { @@ -54,8 +60,14 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT VOID *tx_thread_secure_stack_start; /* Thread's secure stack start address */ VOID *tx_thread_secure_stack_limit; /* Thread's secure stack limit */ TX_THREAD *tx_thread_ptr; /* Keep track of thread for error handling */ + INT tx_next_free_index; /* Next free index of free secure context */ } TX_THREAD_SECURE_STACK_INFO; +/* Static secure contexts */ +static TX_THREAD_SECURE_STACK_INFO tx_thread_secure_context[TX_MAX_SECURE_CONTEXTS]; +/* Head of free secure context */ +static INT tx_head_free_index = 0U; + /**************************************************************************/ @@ -63,7 +75,7 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_initialize Cortex-M33/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -101,12 +113,16 @@ typedef struct TX_THREAD_SECURE_STACK_INFO_STRUCT /* 06-02-2021 Scott Larson Change name, execute in */ /* handler mode, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) UINT _tx_thread_secure_mode_stack_initialize(void) { UINT status; +INT index; /* Make sure function is called from interrupt (threads should not call). */ if (__get_IPSR() == 0) @@ -117,12 +133,26 @@ UINT status; { /* Set secure mode to use PSP. */ __set_CONTROL(__get_CONTROL() | 2); - + /* Set process stack pointer and stack limit to 0 to throw exception when a thread without a secure stack calls a secure function that tries to use secure stack. */ __set_PSPLIM(0); __set_PSP(0); - + + for (index = 0; index < TX_MAX_SECURE_CONTEXTS; index++) + { + + /* Check last index and mark next free to invalid index */ + if(index == (TX_MAX_SECURE_CONTEXTS - 1)) + { + tx_thread_secure_context[index].tx_next_free_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + else + { + tx_thread_secure_context[index].tx_next_free_index = index + 1; + } + } + status = TX_SUCCESS; } return status; @@ -135,7 +165,7 @@ UINT status; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_allocate Cortex-M33/IAR */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -159,9 +189,7 @@ UINT status; /* CALLS */ /* */ /* __get_IPSR Intrinsic to get IPSR */ -/* calloc Compiler's calloc function */ /* malloc Compiler's malloc function */ -/* free Compiler's free() function */ /* __set_PSPLIM Intrinsic to set PSP limit */ /* __set_PSP Intrinsic to set PSP */ /* __TZ_get_PSPLIM_NS Intrinsic to get NS PSP */ @@ -178,6 +206,9 @@ UINT status; /* 10-16-2020 Scott Larson Modified comment(s), */ /* added stack sealing, */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -186,9 +217,10 @@ UINT _tx_thread_secure_mode_stack_allocate(TX_THREAD *thread_ptr, ULONG stack UINT status; TX_THREAD_SECURE_STACK_INFO *info_ptr; UCHAR *stack_mem; +INT secure_context_index; status = TX_SUCCESS; - + /* Make sure function is called from interrupt (threads should not call). */ if (__get_IPSR() == 0) { @@ -198,23 +230,38 @@ UCHAR *stack_mem; { status = TX_SIZE_ERROR; } - + /* Check if thread already has secure stack allocated. */ else if (thread_ptr -> tx_thread_secure_stack_context != 0) { status = TX_THREAD_ERROR; } - + else { - /* Allocate space for secure stack info. */ - info_ptr = calloc(1, sizeof(TX_THREAD_SECURE_STACK_INFO)); - - if(info_ptr != TX_NULL) + TX_DISABLE + + /* Allocate free index for secure stack info. */ + if(tx_head_free_index != TX_INVALID_SECURE_CONTEXT_IDX) { + secure_context_index = tx_head_free_index; + tx_head_free_index = tx_thread_secure_context[tx_head_free_index].tx_next_free_index; + tx_thread_secure_context[secure_context_index].tx_next_free_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + else + { + secure_context_index = TX_INVALID_SECURE_CONTEXT_IDX; + } + + TX_RESTORE + + if(secure_context_index != TX_INVALID_SECURE_CONTEXT_IDX) + { + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* If stack info allocated, allocate a stack & seal. */ stack_mem = malloc(stack_size + TX_THREAD_STACK_SEAL_SIZE); - + if(stack_mem != TX_NULL) { /* Secure stack has been allocated, save in the stack info struct. */ @@ -222,13 +269,13 @@ UCHAR *stack_mem; info_ptr -> tx_thread_secure_stack_start = stack_mem + stack_size; info_ptr -> tx_thread_secure_stack_ptr = info_ptr -> tx_thread_secure_stack_start; info_ptr -> tx_thread_ptr = thread_ptr; - + /* Seal bottom of stack. */ *(ULONG*)info_ptr -> tx_thread_secure_stack_start = TX_THREAD_STACK_SEAL_VALUE; - - /* Save info pointer in thread. */ - thread_ptr -> tx_thread_secure_stack_context = info_ptr; - + + /* Save secure context id (i.e non-zero base index) in thread. */ + thread_ptr -> tx_thread_secure_stack_context = (VOID *)(secure_context_index + 1); + /* Check if this thread is running by looking at its stack start and PSPLIM_NS */ if(((ULONG) thread_ptr -> tx_thread_stack_start & 0xFFFFFFF8) == __TZ_get_PSPLIM_NS()) { @@ -237,21 +284,26 @@ UCHAR *stack_mem; __set_PSP((ULONG)(info_ptr -> tx_thread_secure_stack_ptr)); } } - + else { + TX_DISABLE + /* Stack not allocated, free the info struct. */ - free(info_ptr); + tx_thread_secure_context[secure_context_index].tx_next_free_index = tx_head_free_index; + tx_head_free_index = secure_context_index; + TX_RESTORE + status = TX_NO_MEMORY; } } - + else { status = TX_NO_MEMORY; } } - + return(status); } @@ -262,7 +314,7 @@ UCHAR *stack_mem; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_mode_stack_free Cortex-M33/IAR */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -297,6 +349,9 @@ UCHAR *stack_mem; /* 09-30-2020 Scott Larson Initial Version 6.1 */ /* 10-16-2020 Scott Larson Modified comment(s), */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -304,37 +359,54 @@ UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr) { UINT status; TX_THREAD_SECURE_STACK_INFO *info_ptr; +INT secure_context_index; status = TX_SUCCESS; - - /* Pickup stack info from thread. */ - info_ptr = thread_ptr -> tx_thread_secure_stack_context; - + + /* Pickup stack info id from thread. */ + secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; + /* Make sure function is called from interrupt (threads should not call). */ if (__get_IPSR() == 0) { status = TX_CALLER_ERROR; } - - /* Check that this secure context is for this thread. */ - else if (info_ptr -> tx_thread_ptr != thread_ptr) + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) { status = TX_THREAD_ERROR; } - else { - - /* Free secure stack. */ - free(info_ptr -> tx_thread_secure_stack_limit); - - /* Free info struct. */ - free(info_ptr); - - /* Clear secure context from thread. */ - thread_ptr -> tx_thread_secure_stack_context = 0; + + /* Pickup stack info from static array of secure contexts. */ + info_ptr = &tx_thread_secure_context[secure_context_index]; + + /* Check that this secure context is for this thread. */ + if (info_ptr -> tx_thread_ptr != thread_ptr) + { + status = TX_THREAD_ERROR; + } + + else + { + + /* Free secure stack. */ + free(info_ptr -> tx_thread_secure_stack_limit); + + TX_DISABLE + + /* Free info struct. */ + tx_thread_secure_context[secure_context_index].tx_next_free_index = tx_head_free_index; + tx_head_free_index = secure_context_index; + TX_RESTORE + + /* Clear secure context from thread. */ + thread_ptr -> tx_thread_secure_stack_context = 0; + } } - + return(status); } @@ -345,7 +417,7 @@ TX_THREAD_SECURE_STACK_INFO *info_ptr; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_stack_context_save Cortex-M33/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -382,6 +454,9 @@ TX_THREAD_SECURE_STACK_INFO *info_ptr; /* resulting in version 6.1.1 */ /* 06-02-2021 Scott Larson Fix stack pointer save, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) @@ -389,38 +464,45 @@ void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr) { TX_THREAD_SECURE_STACK_INFO *info_ptr; ULONG sp; +INT secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; /* This function should be called from scheduler only. */ if (__get_IPSR() == 0) { return; } - + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) + { + return; + } + /* Pickup the secure context pointer. */ - info_ptr = (TX_THREAD_SECURE_STACK_INFO *)(thread_ptr -> tx_thread_secure_stack_context); - + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* Check that this secure context is for this thread. */ if (info_ptr -> tx_thread_ptr != thread_ptr) { return; } - + /* Check that stack pointer is in range */ sp = __get_PSP(); - if ((sp < (ULONG)info_ptr -> tx_thread_secure_stack_limit) || + if ((sp < (ULONG)info_ptr -> tx_thread_secure_stack_limit) || (sp > (ULONG)info_ptr -> tx_thread_secure_stack_start)) { return; } - + /* Save stack pointer. */ info_ptr -> tx_thread_secure_stack_ptr = (VOID *) sp; - + /* Set process stack pointer and stack limit to 0 to throw exception when a thread without a secure stack calls a secure function that tries to use secure stack. */ __set_PSPLIM(0); __set_PSP(0); - + return; } @@ -431,7 +513,7 @@ ULONG sp; /* FUNCTION RELEASE */ /* */ /* _tx_thread_secure_stack_context_restore Cortex-M33/IAR */ -/* 6.1.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -465,32 +547,42 @@ ULONG sp; /* 09-30-2020 Scott Larson Initial Version 6.1 */ /* 10-16-2020 Scott Larson Modified comment(s), */ /* resulting in version 6.1.1 */ +/* 01-31-2022 Himanshu Gupta Modified comments(s), updated */ +/* secure stack allocation, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ __attribute__((cmse_nonsecure_entry)) void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr) { TX_THREAD_SECURE_STACK_INFO *info_ptr; +INT secure_context_index = (INT)thread_ptr -> tx_thread_secure_stack_context - 1; /* This function should be called from scheduler only. */ if (__get_IPSR() == 0) { return; } - + + /* Check if secure context index is in valid range. */ + else if (secure_context_index < 0 || secure_context_index >= TX_MAX_SECURE_CONTEXTS) + { + return; + } + /* Pickup the secure context pointer. */ - info_ptr = (TX_THREAD_SECURE_STACK_INFO *)(thread_ptr -> tx_thread_secure_stack_context); - + info_ptr = &tx_thread_secure_context[secure_context_index]; + /* Check that this secure context is for this thread. */ if (info_ptr -> tx_thread_ptr != thread_ptr) { return; } - + /* Set stack pointer and limit. */ __set_PSPLIM((ULONG)info_ptr -> tx_thread_secure_stack_limit); __set_PSP ((ULONG)info_ptr -> tx_thread_secure_stack_ptr); - + return; } diff --git a/ports/cortex_m4/ac5/inc/tx_port.h b/ports/cortex_m4/ac5/inc/tx_port.h index b799f362..0a08d098 100644 --- a/ports/cortex_m4/ac5/inc/tx_port.h +++ b/ports/cortex_m4/ac5/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M4/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m4/ac5/src/tx_timer_interrupt.s b/ports/cortex_m4/ac5/src/tx_timer_interrupt.s index 7e694b07..0fc3a6c3 100644 --- a/ports/cortex_m4/ac5/src/tx_timer_interrupt.s +++ b/ports/cortex_m4/ac5/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER EXPORT _tx_timer_interrupt _tx_timer_interrupt @@ -249,6 +253,7 @@ __tx_timer_nothing_expired DSB // Complete all memory access BX lr // Return to caller // } +#endif ALIGN LTORG END diff --git a/ports/cortex_m4/ac6/inc/tx_port.h b/ports/cortex_m4/ac6/inc/tx_port.h index 20330627..51947682 100644 --- a/ports/cortex_m4/ac6/inc/tx_port.h +++ b/ports/cortex_m4/ac6/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M4/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m4/ac6/src/tx_timer_interrupt.S b/ports/cortex_m4/ac6/src/tx_timer_interrupt.S index 6116f875..9385e1cb 100644 --- a/ports/cortex_m4/ac6/src/tx_timer_interrupt.S +++ b/ports/cortex_m4/ac6/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports/cortex_m4/ghs/example_build/sample_threadx.ld b/ports/cortex_m4/ghs/example_build/sample_threadx.ld index a5cfce73..ba1a8f26 100644 --- a/ports/cortex_m4/ghs/example_build/sample_threadx.ld +++ b/ports/cortex_m4/ghs/example_build/sample_threadx.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x10000) : .stack align(16) pad(0x1000) : .free_mem align(16) pad(0x10000) : diff --git a/ports/cortex_m4/ghs/example_build/sample_threadx_el.ld b/ports/cortex_m4/ghs/example_build/sample_threadx_el.ld index 753374c7..bf1918cd 100644 --- a/ports/cortex_m4/ghs/example_build/sample_threadx_el.ld +++ b/ports/cortex_m4/ghs/example_build/sample_threadx_el.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x1000) : .stack align(16) pad(0x1000) : .eventlog align(16) pad(0x10000) : diff --git a/ports/cortex_m4/gnu/example_build/libc.a b/ports/cortex_m4/gnu/example_build/libc.a deleted file mode 100644 index 6c1567d15e3b2f049d1acd9038ac4fcd28005c18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 743432 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 O)*~DyMuzP+-v0+$^S6Nj diff --git a/ports/cortex_m4/gnu/inc/tx_port.h b/ports/cortex_m4/gnu/inc/tx_port.h index 8bfa5a7d..80237531 100644 --- a/ports/cortex_m4/gnu/inc/tx_port.h +++ b/ports/cortex_m4/gnu/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M4/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m4/gnu/src/tx_thread_schedule.S b/ports/cortex_m4/gnu/src/tx_thread_schedule.S index 2bf7656f..63e5c01f 100644 --- a/ports/cortex_m4/gnu/src/tx_thread_schedule.S +++ b/ports/cortex_m4/gnu/src/tx_thread_schedule.S @@ -37,7 +37,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M4/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -64,13 +64,14 @@ /* */ /* _tx_initialize_kernel_enter ThreadX entry function */ /* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -91,7 +92,7 @@ _tx_thread_schedule: /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit MSR CONTROL, r0 // Setup new CONTROL register @@ -151,7 +152,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -213,7 +214,7 @@ __tx_ts_restore: LDR r12, [r1, #8] // Pickup thread's stack pointer LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers @@ -271,7 +272,7 @@ __tx_ts_ready: B __tx_ts_restore // Restore the thread // } -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP .global tx_thread_fpu_enable .thumb_func diff --git a/ports/cortex_m4/gnu/src/tx_timer_interrupt.S b/ports/cortex_m4/gnu/src/tx_timer_interrupt.S index 810f959e..043921be 100644 --- a/ports/cortex_m4/gnu/src/tx_timer_interrupt.S +++ b/ports/cortex_m4/gnu/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports/cortex_m4/iar/inc/tx_port.h b/ports/cortex_m4/iar/inc/tx_port.h index 1c315b1e..67b7fa60 100644 --- a/ports/cortex_m4/iar/inc/tx_port.h +++ b/ports/cortex_m4/iar/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M4/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m4/iar/src/tx_timer_interrupt.s b/ports/cortex_m4/iar/src/tx_timer_interrupt.s index f99d1755..1684e923 100644 --- a/ports/cortex_m4/iar/src/tx_timer_interrupt.s +++ b/ports/cortex_m4/iar/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER PUBLIC _tx_timer_interrupt _tx_timer_interrupt: @@ -249,4 +253,5 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif END diff --git a/ports/cortex_m4/keil/inc/tx_port.h b/ports/cortex_m4/keil/inc/tx_port.h index 18916ac8..8c6f8c8c 100644 --- a/ports/cortex_m4/keil/inc/tx_port.h +++ b/ports/cortex_m4/keil/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M4/Keil */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m7/ac5/inc/tx_port.h b/ports/cortex_m7/ac5/inc/tx_port.h index a797ff9c..391fd743 100644 --- a/ports/cortex_m7/ac5/inc/tx_port.h +++ b/ports/cortex_m7/ac5/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M7/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m7/ac5/src/tx_timer_interrupt.s b/ports/cortex_m7/ac5/src/tx_timer_interrupt.s index 38fec7e4..6c83c737 100644 --- a/ports/cortex_m7/ac5/src/tx_timer_interrupt.s +++ b/ports/cortex_m7/ac5/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER EXPORT _tx_timer_interrupt _tx_timer_interrupt @@ -249,6 +253,7 @@ __tx_timer_nothing_expired DSB // Complete all memory access BX lr // Return to caller // } +#endif ALIGN LTORG END diff --git a/ports/cortex_m7/ac6/inc/tx_port.h b/ports/cortex_m7/ac6/inc/tx_port.h index 6a3d8e82..904a12b3 100644 --- a/ports/cortex_m7/ac6/inc/tx_port.h +++ b/ports/cortex_m7/ac6/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M7/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m7/ac6/src/tx_timer_interrupt.S b/ports/cortex_m7/ac6/src/tx_timer_interrupt.S index c8ab5724..bde18d04 100644 --- a/ports/cortex_m7/ac6/src/tx_timer_interrupt.S +++ b/ports/cortex_m7/ac6/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports/cortex_m7/ghs/example_build/sample_threadx.ld b/ports/cortex_m7/ghs/example_build/sample_threadx.ld index a5cfce73..ba1a8f26 100644 --- a/ports/cortex_m7/ghs/example_build/sample_threadx.ld +++ b/ports/cortex_m7/ghs/example_build/sample_threadx.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x10000) : .stack align(16) pad(0x1000) : .free_mem align(16) pad(0x10000) : diff --git a/ports/cortex_m7/ghs/example_build/sample_threadx_el.ld b/ports/cortex_m7/ghs/example_build/sample_threadx_el.ld index 753374c7..bf1918cd 100644 --- a/ports/cortex_m7/ghs/example_build/sample_threadx_el.ld +++ b/ports/cortex_m7/ghs/example_build/sample_threadx_el.ld @@ -19,25 +19,25 @@ -sec { - .reset 0x000000 : - .picbase 0x1000 : - .text : + .reset 0x000000 : + .picbase 0x1000 : + .text : .comment : .intercall : .interfunc : - .syscall : + .syscall : .fixaddr : .fixtype : - .rodata : + .rodata : .romdata ROM(.data) : .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : .heap align(16) pad(0x1000) : .stack align(16) pad(0x1000) : .eventlog align(16) pad(0x10000) : diff --git a/ports/cortex_m7/gnu/example_build/libc.a b/ports/cortex_m7/gnu/example_build/libc.a deleted file mode 100644 index 6c1567d15e3b2f049d1acd9038ac4fcd28005c18..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 743432 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 O)*~DyMuzP+-v0+$^S6Nj diff --git a/ports/cortex_m7/gnu/inc/tx_port.h b/ports/cortex_m7/gnu/inc/tx_port.h index aeb740c1..1c351bb8 100644 --- a/ports/cortex_m7/gnu/inc/tx_port.h +++ b/ports/cortex_m7/gnu/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M7/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m7/gnu/src/tx_thread_schedule.S b/ports/cortex_m7/gnu/src/tx_thread_schedule.S index e9872072..f75bff29 100644 --- a/ports/cortex_m7/gnu/src/tx_thread_schedule.S +++ b/ports/cortex_m7/gnu/src/tx_thread_schedule.S @@ -37,7 +37,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M7/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -64,13 +64,14 @@ /* */ /* _tx_initialize_kernel_enter ThreadX entry function */ /* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -91,7 +92,7 @@ _tx_thread_schedule: /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit MSR CONTROL, r0 // Setup new CONTROL register @@ -151,7 +152,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -213,7 +214,7 @@ __tx_ts_restore: LDR r12, [r1, #8] // Pickup thread's stack pointer LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers @@ -271,7 +272,7 @@ __tx_ts_ready: B __tx_ts_restore // Restore the thread // } -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP .global tx_thread_fpu_enable .thumb_func diff --git a/ports/cortex_m7/gnu/src/tx_timer_interrupt.S b/ports/cortex_m7/gnu/src/tx_timer_interrupt.S index 7cf4d4f3..d0fc6929 100644 --- a/ports/cortex_m7/gnu/src/tx_timer_interrupt.S +++ b/ports/cortex_m7/gnu/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports/cortex_m7/iar/inc/tx_port.h b/ports/cortex_m7/iar/inc/tx_port.h index f950ba37..f5a144cd 100644 --- a/ports/cortex_m7/iar/inc/tx_port.h +++ b/ports/cortex_m7/iar/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h Cortex-M7/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -51,6 +51,11 @@ /* DATE NAME DESCRIPTION */ /* */ /* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -277,7 +282,7 @@ void __iar_Initlocks(void); #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -366,9 +371,9 @@ void _tx_vfp_access(void); if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ else \ { \ @@ -378,14 +383,14 @@ void _tx_vfp_access(void); if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = __get_control_value(); \ _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_control_value(_tx_vfp_state); \ + __set_control_value(_tx_vfp_state); \ } \ } \ } \ @@ -429,7 +434,7 @@ void _tx_vfp_access(void); #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -590,7 +595,7 @@ unsigned int interrupt_save; } } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); @@ -663,7 +668,7 @@ unsigned int interrupt_save; } -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; #define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); diff --git a/ports/cortex_m7/iar/src/tx_timer_interrupt.s b/ports/cortex_m7/iar/src/tx_timer_interrupt.s index 7c899085..27396fd0 100644 --- a/ports/cortex_m7/iar/src/tx_timer_interrupt.s +++ b/ports/cortex_m7/iar/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER PUBLIC _tx_timer_interrupt _tx_timer_interrupt: @@ -249,4 +253,5 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif END diff --git a/ports/cortex_r4/green/example_build/azure_rtos_workspace.gpj b/ports/cortex_r4/ghs/example_build/azure_rtos_workspace.gpj similarity index 100% rename from ports/cortex_r4/green/example_build/azure_rtos_workspace.gpj rename to ports/cortex_r4/ghs/example_build/azure_rtos_workspace.gpj diff --git a/ports/cortex_r4/green/example_build/reset.arm b/ports/cortex_r4/ghs/example_build/reset.arm similarity index 100% rename from ports/cortex_r4/green/example_build/reset.arm rename to ports/cortex_r4/ghs/example_build/reset.arm diff --git a/ports/cortex_r4/ghs/example_build/sample_threadx.c b/ports/cortex_r4/ghs/example_build/sample_threadx.c new file mode 100644 index 00000000..8c61de06 --- /dev/null +++ b/ports/cortex_r4/ghs/example_build/sample_threadx.c @@ -0,0 +1,369 @@ +/* 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; + + +/* 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", first_unused_memory, 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_r4/green/example_build/sample_threadx.con b/ports/cortex_r4/ghs/example_build/sample_threadx.con similarity index 87% rename from ports/cortex_r4/green/example_build/sample_threadx.con rename to ports/cortex_r4/ghs/example_build/sample_threadx.con index 1fbf19f9..d64860de 100644 --- a/ports/cortex_r4/green/example_build/sample_threadx.con +++ b/ports/cortex_r4/ghs/example_build/sample_threadx.con @@ -1,4 +1,4 @@ -target_connection.00000000.title="Simulator connection for ThreadX" +target_connection.00000000.title="Simulator" target_connection.00000000.type="Custom" target_connection.00000000.short_type="Custom" target_connection.00000000.args="simarm -cpu=cortexr4 -fpu -rom" diff --git a/ports/cortex_r4/green/example_build/sample_threadx.gpj b/ports/cortex_r4/ghs/example_build/sample_threadx.gpj similarity index 100% rename from ports/cortex_r4/green/example_build/sample_threadx.gpj rename to ports/cortex_r4/ghs/example_build/sample_threadx.gpj diff --git a/ports/cortex_r4/ghs/example_build/sample_threadx.ld b/ports/cortex_r4/ghs/example_build/sample_threadx.ld new file mode 100644 index 00000000..a93f11ea --- /dev/null +++ b/ports/cortex_r4/ghs/example_build/sample_threadx.ld @@ -0,0 +1,44 @@ +# The following explains what the default Green Hills sections are for: +# +# picbase - base of the text sections, relocatable in -pic mode +# text - text section +# syscall - syscall section, for host I/O under Multi +# fixaddr/fixtype - for PIC/PID fixups +# rodata - read only data +# romdata - the ROM image of .data +# romsdata - the ROM image of .sdata +# secinfo - section information section, used by the start-up code +# pidbase - base of the data sections, relocatable in -pid mode +# sdabase - base of the small data area section pointer +# sbss - small BSS (zeroed data) section +# sdata - small data section +# data - non-zeroed writeable data section +# bss - zeroed data section +# heap - the heap, grows upward +# stack - the stack, grows downward + +-sec +{ + .reset 0x000000 : + .picbase 0x1000 : + .text : + .comment : + .intercall : + .interfunc : + .syscall : + .fixaddr : + .fixtype : + .rodata : + .romdata ROM(.data) : + .romsdata ROM(.sdata) : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : + .heap align(16) pad(0x10000) : + .stack align(16) pad(0x1000) : + .free_mem align(16) pad(0x10000) : +} diff --git a/ports/cortex_r4/green/example_build/sample_threadx_el.gpj b/ports/cortex_r4/ghs/example_build/sample_threadx_el.gpj similarity index 100% rename from ports/cortex_r4/green/example_build/sample_threadx_el.gpj rename to ports/cortex_r4/ghs/example_build/sample_threadx_el.gpj diff --git a/ports/cortex_r4/ghs/example_build/sample_threadx_el.ld b/ports/cortex_r4/ghs/example_build/sample_threadx_el.ld new file mode 100644 index 00000000..65d9de03 --- /dev/null +++ b/ports/cortex_r4/ghs/example_build/sample_threadx_el.ld @@ -0,0 +1,45 @@ +# The following explains what the default Green Hills sections are for: +# +# picbase - base of the text sections, relocatable in -pic mode +# text - text section +# syscall - syscall section, for host I/O under Multi +# fixaddr/fixtype - for PIC/PID fixups +# rodata - read only data +# romdata - the ROM image of .data +# romsdata - the ROM image of .sdata +# secinfo - section information section, used by the start-up code +# pidbase - base of the data sections, relocatable in -pid mode +# sdabase - base of the small data area section pointer +# sbss - small BSS (zeroed data) section +# sdata - small data section +# data - non-zeroed writeable data section +# bss - zeroed data section +# heap - the heap, grows upward +# stack - the stack, grows downward + +-sec +{ + .reset 0x000000 : + .picbase 0x1000 : + .text : + .comment : + .intercall : + .interfunc : + .syscall : + .fixaddr : + .fixtype : + .rodata : + .romdata ROM(.data) : + .romsdata ROM(.sdata) : + .secinfo : + .pidbase align(16) : + .sdabase : + .sbss : + .sdata : + .data : + .bss : + .heap align(16) pad(0x1000) : + .stack align(16) pad(0x1000) : + .eventlog align(16) pad(0x10000) : + .free_mem align(16) pad(0x10000) : +} diff --git a/ports/cortex_r4/ghs/example_build/tx.gpj b/ports/cortex_r4/ghs/example_build/tx.gpj new file mode 100644 index 00000000..a8a7bbb8 --- /dev/null +++ b/ports/cortex_r4/ghs/example_build/tx.gpj @@ -0,0 +1,222 @@ +#!gbuild +[Library] + -I../../../../common/inc + -I../inc +..\..\..\..\common\inc\tx_api.h +..\..\..\..\common\inc\tx_block_pool.h +..\..\..\..\common\inc\tx_byte_pool.h +..\..\..\..\common\inc\tx_event_flags.h +..\..\..\..\common\inc\tx_initialize.h +..\..\..\..\common\inc\tx_mutex.h +..\..\..\..\common\inc\tx_queue.h +..\..\..\..\common\inc\tx_semaphore.h +..\..\..\..\common\inc\tx_thread.h +..\..\..\..\common\inc\tx_timer.h +..\..\..\..\common\inc\tx_trace.h +..\..\..\..\common\inc\tx_user_sample.h +..\inc\tx_port.h +..\inc\tx_el.h +..\inc\tx_ghs.h +..\src\tx_el.c +..\src\tx_ghs.c +..\src\tx_ghse.c +..\src\tx_thread_context_restore.arm +..\src\tx_thread_context_save.arm +..\src\tx_thread_fiq_context_restore.arm +..\src\tx_thread_fiq_context_save.arm +..\src\tx_thread_fiq_nesting_end.arm +..\src\tx_thread_fiq_nesting_start.arm +..\src\tx_thread_interrupt_control.arm +..\src\tx_thread_interrupt_disable.arm +..\src\tx_thread_interrupt_restore.arm +..\src\tx_thread_irq_nesting_end.arm +..\src\tx_thread_irq_nesting_start.arm +..\src\tx_thread_schedule.arm +..\src\tx_thread_stack_build.arm +..\src\tx_thread_system_return.arm +..\src\tx_thread_vectored_context_save.arm +..\src\tx_timer_interrupt.arm +..\..\..\..\common\src\tx_block_allocate.c +..\..\..\..\common\src\tx_block_pool_cleanup.c +..\..\..\..\common\src\tx_block_pool_create.c +..\..\..\..\common\src\tx_block_pool_delete.c +..\..\..\..\common\src\tx_block_pool_info_get.c +..\..\..\..\common\src\tx_block_pool_initialize.c +..\..\..\..\common\src\tx_block_pool_performance_info_get.c +..\..\..\..\common\src\tx_block_pool_performance_system_info_get.c +..\..\..\..\common\src\tx_block_pool_prioritize.c +..\..\..\..\common\src\tx_block_release.c +..\..\..\..\common\src\tx_byte_allocate.c +..\..\..\..\common\src\tx_byte_pool_cleanup.c +..\..\..\..\common\src\tx_byte_pool_create.c +..\..\..\..\common\src\tx_byte_pool_delete.c +..\..\..\..\common\src\tx_byte_pool_info_get.c +..\..\..\..\common\src\tx_byte_pool_initialize.c +..\..\..\..\common\src\tx_byte_pool_performance_info_get.c +..\..\..\..\common\src\tx_byte_pool_performance_system_info_get.c +..\..\..\..\common\src\tx_byte_pool_prioritize.c +..\..\..\..\common\src\tx_byte_pool_search.c +..\..\..\..\common\src\tx_byte_release.c +..\..\..\..\common\src\tx_event_flags_cleanup.c +..\..\..\..\common\src\tx_event_flags_create.c +..\..\..\..\common\src\tx_event_flags_delete.c +..\..\..\..\common\src\tx_event_flags_get.c +..\..\..\..\common\src\tx_event_flags_info_get.c +..\..\..\..\common\src\tx_event_flags_initialize.c +..\..\..\..\common\src\tx_event_flags_performance_info_get.c +..\..\..\..\common\src\tx_event_flags_performance_system_info_get.c +..\..\..\..\common\src\tx_event_flags_set.c +..\..\..\..\common\src\tx_event_flags_set_notify.c +..\..\..\..\common\src\tx_initialize_high_level.c +..\..\..\..\common\src\tx_initialize_kernel_enter.c +..\..\..\..\common\src\tx_initialize_kernel_setup.c +..\..\..\..\common\src\tx_mutex_cleanup.c +..\..\..\..\common\src\tx_mutex_create.c +..\..\..\..\common\src\tx_mutex_delete.c +..\..\..\..\common\src\tx_mutex_get.c +..\..\..\..\common\src\tx_mutex_info_get.c +..\..\..\..\common\src\tx_mutex_initialize.c +..\..\..\..\common\src\tx_mutex_performance_info_get.c +..\..\..\..\common\src\tx_mutex_performance_system_info_get.c +..\..\..\..\common\src\tx_mutex_prioritize.c +..\..\..\..\common\src\tx_mutex_priority_change.c +..\..\..\..\common\src\tx_mutex_put.c +..\..\..\..\common\src\tx_queue_cleanup.c +..\..\..\..\common\src\tx_queue_create.c +..\..\..\..\common\src\tx_queue_delete.c +..\..\..\..\common\src\tx_queue_flush.c +..\..\..\..\common\src\tx_queue_front_send.c +..\..\..\..\common\src\tx_queue_info_get.c +..\..\..\..\common\src\tx_queue_initialize.c +..\..\..\..\common\src\tx_queue_performance_info_get.c +..\..\..\..\common\src\tx_queue_performance_system_info_get.c +..\..\..\..\common\src\tx_queue_prioritize.c +..\..\..\..\common\src\tx_queue_receive.c +..\..\..\..\common\src\tx_queue_send.c +..\..\..\..\common\src\tx_queue_send_notify.c +..\..\..\..\common\src\tx_semaphore_ceiling_put.c +..\..\..\..\common\src\tx_semaphore_cleanup.c +..\..\..\..\common\src\tx_semaphore_create.c +..\..\..\..\common\src\tx_semaphore_delete.c +..\..\..\..\common\src\tx_semaphore_get.c +..\..\..\..\common\src\tx_semaphore_info_get.c +..\..\..\..\common\src\tx_semaphore_initialize.c +..\..\..\..\common\src\tx_semaphore_performance_info_get.c +..\..\..\..\common\src\tx_semaphore_performance_system_info_get.c +..\..\..\..\common\src\tx_semaphore_prioritize.c +..\..\..\..\common\src\tx_semaphore_put.c +..\..\..\..\common\src\tx_semaphore_put_notify.c +..\..\..\..\common\src\tx_thread_create.c +..\..\..\..\common\src\tx_thread_delete.c +..\..\..\..\common\src\tx_thread_entry_exit_notify.c +..\..\..\..\common\src\tx_thread_identify.c +..\..\..\..\common\src\tx_thread_info_get.c +..\..\..\..\common\src\tx_thread_initialize.c +..\..\..\..\common\src\tx_thread_performance_info_get.c +..\..\..\..\common\src\tx_thread_performance_system_info_get.c +..\..\..\..\common\src\tx_thread_preemption_change.c +..\..\..\..\common\src\tx_thread_priority_change.c +..\..\..\..\common\src\tx_thread_relinquish.c +..\..\..\..\common\src\tx_thread_reset.c +..\..\..\..\common\src\tx_thread_resume.c +..\..\..\..\common\src\tx_thread_shell_entry.c +..\..\..\..\common\src\tx_thread_sleep.c +..\..\..\..\common\src\tx_thread_stack_analyze.c +..\..\..\..\common\src\tx_thread_stack_error_handler.c +..\..\..\..\common\src\tx_thread_stack_error_notify.c +..\..\..\..\common\src\tx_thread_suspend.c +..\..\..\..\common\src\tx_thread_system_preempt_check.c +..\..\..\..\common\src\tx_thread_system_resume.c +..\..\..\..\common\src\tx_thread_system_suspend.c +..\..\..\..\common\src\tx_thread_terminate.c +..\..\..\..\common\src\tx_thread_time_slice.c +..\..\..\..\common\src\tx_thread_time_slice_change.c +..\..\..\..\common\src\tx_thread_timeout.c +..\..\..\..\common\src\tx_thread_wait_abort.c +..\..\..\..\common\src\tx_time_get.c +..\..\..\..\common\src\tx_time_set.c +..\..\..\..\common\src\tx_timer_activate.c +..\..\..\..\common\src\tx_timer_change.c +..\..\..\..\common\src\tx_timer_create.c +..\..\..\..\common\src\tx_timer_deactivate.c +..\..\..\..\common\src\tx_timer_delete.c +..\..\..\..\common\src\tx_timer_expiration_process.c +..\..\..\..\common\src\tx_timer_info_get.c +..\..\..\..\common\src\tx_timer_initialize.c +..\..\..\..\common\src\tx_timer_performance_info_get.c +..\..\..\..\common\src\tx_timer_performance_system_info_get.c +..\..\..\..\common\src\tx_timer_system_activate.c +..\..\..\..\common\src\tx_timer_system_deactivate.c +..\..\..\..\common\src\tx_timer_thread_entry.c +..\..\..\..\common\src\tx_trace_buffer_full_notify.c +..\..\..\..\common\src\tx_trace_disable.c +..\..\..\..\common\src\tx_trace_enable.c +..\..\..\..\common\src\tx_trace_event_filter.c +..\..\..\..\common\src\tx_trace_event_unfilter.c +..\..\..\..\common\src\tx_trace_initialize.c +..\..\..\..\common\src\tx_trace_interrupt_control.c +..\..\..\..\common\src\tx_trace_isr_enter_insert.c +..\..\..\..\common\src\tx_trace_isr_exit_insert.c +..\..\..\..\common\src\tx_trace_object_register.c +..\..\..\..\common\src\tx_trace_object_unregister.c +..\..\..\..\common\src\tx_trace_user_event_insert.c +..\..\..\..\common\src\txe_block_allocate.c +..\..\..\..\common\src\txe_block_pool_create.c +..\..\..\..\common\src\txe_block_pool_delete.c +..\..\..\..\common\src\txe_block_pool_info_get.c +..\..\..\..\common\src\txe_block_pool_prioritize.c +..\..\..\..\common\src\txe_block_release.c +..\..\..\..\common\src\txe_byte_allocate.c +..\..\..\..\common\src\txe_byte_pool_create.c +..\..\..\..\common\src\txe_byte_pool_delete.c +..\..\..\..\common\src\txe_byte_pool_info_get.c +..\..\..\..\common\src\txe_byte_pool_prioritize.c +..\..\..\..\common\src\txe_byte_release.c +..\..\..\..\common\src\txe_event_flags_create.c +..\..\..\..\common\src\txe_event_flags_delete.c +..\..\..\..\common\src\txe_event_flags_get.c +..\..\..\..\common\src\txe_event_flags_info_get.c +..\..\..\..\common\src\txe_event_flags_set.c +..\..\..\..\common\src\txe_event_flags_set_notify.c +..\..\..\..\common\src\txe_mutex_create.c +..\..\..\..\common\src\txe_mutex_delete.c +..\..\..\..\common\src\txe_mutex_get.c +..\..\..\..\common\src\txe_mutex_info_get.c +..\..\..\..\common\src\txe_mutex_prioritize.c +..\..\..\..\common\src\txe_mutex_put.c +..\..\..\..\common\src\txe_queue_create.c +..\..\..\..\common\src\txe_queue_delete.c +..\..\..\..\common\src\txe_queue_flush.c +..\..\..\..\common\src\txe_queue_front_send.c +..\..\..\..\common\src\txe_queue_info_get.c +..\..\..\..\common\src\txe_queue_prioritize.c +..\..\..\..\common\src\txe_queue_receive.c +..\..\..\..\common\src\txe_queue_send.c +..\..\..\..\common\src\txe_queue_send_notify.c +..\..\..\..\common\src\txe_semaphore_ceiling_put.c +..\..\..\..\common\src\txe_semaphore_create.c +..\..\..\..\common\src\txe_semaphore_delete.c +..\..\..\..\common\src\txe_semaphore_get.c +..\..\..\..\common\src\txe_semaphore_info_get.c +..\..\..\..\common\src\txe_semaphore_prioritize.c +..\..\..\..\common\src\txe_semaphore_put.c +..\..\..\..\common\src\txe_semaphore_put_notify.c +..\..\..\..\common\src\txe_thread_create.c +..\..\..\..\common\src\txe_thread_delete.c +..\..\..\..\common\src\txe_thread_entry_exit_notify.c +..\..\..\..\common\src\txe_thread_info_get.c +..\..\..\..\common\src\txe_thread_preemption_change.c +..\..\..\..\common\src\txe_thread_priority_change.c +..\..\..\..\common\src\txe_thread_relinquish.c +..\..\..\..\common\src\txe_thread_reset.c +..\..\..\..\common\src\txe_thread_resume.c +..\..\..\..\common\src\txe_thread_suspend.c +..\..\..\..\common\src\txe_thread_terminate.c +..\..\..\..\common\src\txe_thread_time_slice_change.c +..\..\..\..\common\src\txe_thread_wait_abort.c +..\..\..\..\common\src\txe_timer_activate.c +..\..\..\..\common\src\txe_timer_change.c +..\..\..\..\common\src\txe_timer_create.c +..\..\..\..\common\src\txe_timer_deactivate.c +..\..\..\..\common\src\txe_timer_delete.c +..\..\..\..\common\src\txe_timer_info_get.c diff --git a/ports/cortex_r4/ghs/example_build/tx_initialize_low_level.arm b/ports/cortex_r4/ghs/example_build/tx_initialize_low_level.arm new file mode 100644 index 00000000..e6bab047 --- /dev/null +++ b/ports/cortex_r4/ghs/example_build/tx_initialize_low_level.arm @@ -0,0 +1,319 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* #define TX_SOURCE_CODE */ + + +/* Include necessary system files. */ + +/* #include "tx_api.h" + #include "tx_initialize.h" + #include "tx_thread.h" + #include "tx_timer.h" */ + + SVC_MODE = 0xD3 # Disable IRQ/FIQ SVC mode + IRQ_MODE = 0xD2 # Disable IRQ/FIQ IRQ mode + FIQ_MODE = 0xD1 # Disable IRQ/FIQ FIQ mode + SYS_MODE = 0xDF # Disable IRQ/FIQ SYS mode + MODE_MASK = 0x1F # Mode mask + FIQ_STACK_SIZE = 512 # FIQ stack size + IRQ_STACK_SIZE = 1024 # IRQ stack size + SYS_STACK_SIZE = 1024 # SYS stack size + + .text + .align 4 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-R4/GHS */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +{ */ + .globl _tx_initialize_low_level +_tx_initialize_low_level: + + /****** NOTE ****** We must be in SVC MODE at this point. Some monitors + enter this routine in USER mode and require a software interrupt to + change into SVC mode. */ + + /* Save the system stack pointer. */ + /* _tx_thread_system_stack_ptr = (VOID_PTR) (sp); */ + + LDR r1,=_tx_thread_system_stack_ptr # Pickup address of system stack ptr + STR sp, [r1] # Save system stack + + /* Pickup the first available memory address. */ + + LDR r0,=__ghsbegin_free_mem # Pickup free memory address + + /* Setup initial stack pointers for IRQ and FIQ modes. */ + + MRS r12, CPSR # Pickup current CPSR + MOV r1, r0 # Get first available memory +#ifdef TX_ENABLE_IRQ_NESTING + /* Setup the system mode stack for nested interrupt support */ + LDR r2, =SYS_STACK_SIZE # Pickup stack size + BIC r3, r12, MODE_MASK # Clear mode bits + ORR r3, r3, SYS_MODE # Build SYS mode CPSR + MSR CPSR_c, r3 # Enter SYS mode + ADD r1, r1, r2 # Calculate start of SYS stack + SUB r1, r1, 1 # Backup one byte + BIC r1, r1, 7 # Insure 8-byte alignment + MOV sp, r1 # Setup SYS stack pointer +#endif + LDR r2, =FIQ_STACK_SIZE # Pickup stack size + BIC r3, r12, MODE_MASK # Clear mode bits + ORR r3, r3, FIQ_MODE # Build FIQ mode CPSR + MSR CPSR_c, r3 # Enter FIQ mode + ADD r1, r1, r2 # Calculate start of FIQ stack + SUB r1, r1, 1 # Backup one byte + BIC r1, r1, 7 # Insure 8-byte alignment + MOV sp, r1 # Setup FIQ stack pointer + MOV r10, 0 # Clear sl + MOV r11, 0 # Clear fp + LDR r2, =IRQ_STACK_SIZE # Pickup IRQ (system stack size) + BIC r3, r12, MODE_MASK # Clear mode bits + ORR r3, r3, IRQ_MODE # Build IRQ mode CPSR + MSR CPSR_c, r3 # Enter IRQ mode + ADD r1, r1, r2 # Calculate start of IRQ stack + SUB r1, r1, 1 # Backup one byte + BIC r1, r1, 7 # Insure 8-byte alignment + MOV sp, r1 # Setup IRQ stack pointer + MSR CPSR_c, r12 # Restore previous mode + ADD r0, r1, 4 # Adjust the new free memory + + + /* Save the first available memory address. */ + /* _tx_initialize_unused_memory = (VOID_PTR) __ghsbegin_free_mem + [SYS_STACK] + FIQ_STACK + IRQ_STACK; */ + + LDR r2,=_tx_initialize_unused_memory # Pickup unused memory ptr address + STR r0, [r2] # Save first free memory address + + + /* Setup Timer for periodic interrupts. To generate timer interrupts with + the Green Hills simulator, enter the following command in the target + window: timer 9999 irq */ + + /* Done, return to caller. */ + + RET # Return to caller + + .type _tx_initialize_low_level,$function + .size _tx_initialize_low_level,.-_tx_initialize_low_level +/* } */ + + +/* Define shells for each of the interrupt vectors. */ + + .globl __tx_undefined +__tx_undefined: + B __tx_undefined # Undefined handler + + .type __tx_undefined,$function + .size __tx_undefined,.-__tx_undefined + + .globl __tx_swi_interrupt +__tx_swi_interrupt: + B __tx_swi_interrupt # Software interrupt handler + + .type __tx_swi_interrupt,$function + .size __tx_swi_interrupt,.-__tx_swi_interrupt + + .globl __tx_prefetch_handler +__tx_prefetch_handler: + B __tx_prefetch_handler # Prefetch exception handler + + .type __tx_prefetch_handler,$function + .size __tx_prefetch_handler,.-__tx_prefetch_handler + + .globl __tx_abort_handler +__tx_abort_handler: + B __tx_abort_handler # Abort exception handler + + .type __tx_abort_handler,$function + .size __tx_abort_handler,.-__tx_abort_handler + + .globl __tx_reserved_handler +__tx_reserved_handler: + B __tx_reserved_handler # Reserved exception handler + + .type __tx_reserved_handler,$function + .size __tx_reserved_handler,.-__tx_reserved_handler + + .globl __tx_irq_handler + .globl __tx_irq_processing_return +__tx_irq_handler: + + /* Jump to context save to save system context. */ + B _tx_thread_context_save + + .type __tx_irq_handler,$function + .size __tx_irq_handler,.-__tx_irq_handler + +__tx_irq_processing_return: + + /* At this point execution is still in the IRQ mode. The CPSR, point of + interrupt, and all C scratch registers are available for use. */ + +#ifdef TX_ENABLE_EVENT_LOGGING + MOV r0, 0 # Build interrupt code + BL _tx_el_interrupt # Call interrupt event logging +#endif + + /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start + from IRQ mode with interrupts disabled. This routine switches to the + system mode and returns with IRQ interrupts enabled. + + NOTE: It is very important to ensure all IRQ interrupts are cleared + prior to enabling nested IRQ interrupts. */ +#ifdef TX_ENABLE_IRQ_NESTING + BL _tx_thread_irq_nesting_start +#endif + + + /* For debug purpose, execute the timer interrupt processing here. In + a real system, some kind of status indication would have to be checked + before the timer interrupt handler could be called. */ + BL _tx_timer_interrupt # Timer interrupt handler + + /* Application IRQ handlers can be called here! */ + + /* If interrupt nesting was started earlier, the end of interrupt nesting + service must be called before returning to _tx_thread_context_restore. + This routine returns in processing in IRQ mode with interrupts disabled. */ +#ifdef TX_ENABLE_IRQ_NESTING + BL _tx_thread_irq_nesting_end +#endif + +#ifdef TX_ENABLE_EVENT_LOGGING + MOV r0, 0 # Build interrupt code + BL _tx_el_interrupt_end # Call interrupt event logging +#endif + + /* Jump to context restore to restore system context. */ + B _tx_thread_context_restore + + .type __tx_irq_processing_return,$function + .size __tx_irq_processing_return,.-__tx_irq_processing_return + +#ifdef TX_ENABLE_FIQ_SUPPORT + + .globl __tx_fiq_handler + .globl __tx_fiq_processing_return +__tx_fiq_handler: + /* Jump to fiq context save to save system context. */ + B _tx_thread_fiq_context_save + + .type __tx_fiq_handler,$function + .size __tx_fiq_handler,.-__tx_fiq_handler + +__tx_fiq_processing_return: + + /* At this point execution is still in the FIQ mode. The CPSR, point of + interrupt, and all C scratch registers are available for use. */ + +#ifdef TX_ENABLE_EVENT_LOGGING + MOV r0, 1 # Build interrupt code + BL _tx_el_interrupt # Call interrupt event logging +#endif + + /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start + from FIQ mode with interrupts disabled. This routine switches to the + system mode and returns with FIQ interrupts enabled. + + NOTE: It is very important to ensure all FIQ interrupts are cleared + prior to enabling nested FIQ interrupts. */ +#ifdef TX_ENABLE_FIQ_NESTING + BL _tx_thread_fiq_nesting_start +#endif + + /* Application FIQ handlers can be called here! */ + + /* If interrupt nesting was started earlier, the end of interrupt nesting + service must be called before returning to _tx_thread_fiq_context_restore. */ +#ifdef TX_ENABLE_FIQ_NESTING + BL _tx_thread_fiq_nesting_end +#endif + +#ifdef TX_ENABLE_EVENT_LOGGING + MOV r0, 1 # Build interrupt code + BL _tx_el_interrupt_end # Call interrupt event logging +#endif + + /* Jump to fiq context restore to restore system context. */ + B _tx_thread_fiq_context_restore + + .type __tx_fiq_processing_return,$function + .size __tx_fiq_processing_return,.-__tx_fiq_processing_return + +#else + .globl __tx_fiq_handler +__tx_fiq_handler: + B __tx_fiq_handler # FIQ interrupt handler + + .type __tx_fiq_handler,$function + .size __tx_fiq_handler,.-__tx_fiq_handler +#endif + + /* Reference build options and version ID to ensure they come in. */ + +BUILD_OPTIONS: + .data.w _tx_build_options +VERSION_ID: + .data.w _tx_version_id diff --git a/ports/cortex_r4/ghs/example_build/txe.gpj b/ports/cortex_r4/ghs/example_build/txe.gpj new file mode 100644 index 00000000..302c0cb2 --- /dev/null +++ b/ports/cortex_r4/ghs/example_build/txe.gpj @@ -0,0 +1,223 @@ +#!gbuild +[Library] + -DTX_ENABLE_EVENT_LOGGING + -I../../../../common/inc + -I../inc +..\..\..\..\common\inc\tx_api.h +..\..\..\..\common\inc\tx_block_pool.h +..\..\..\..\common\inc\tx_byte_pool.h +..\..\..\..\common\inc\tx_event_flags.h +..\..\..\..\common\inc\tx_initialize.h +..\..\..\..\common\inc\tx_mutex.h +..\..\..\..\common\inc\tx_queue.h +..\..\..\..\common\inc\tx_semaphore.h +..\..\..\..\common\inc\tx_thread.h +..\..\..\..\common\inc\tx_timer.h +..\..\..\..\common\inc\tx_trace.h +..\..\..\..\common\inc\tx_user_sample.h +..\inc\tx_port.h +..\inc\tx_el.h +..\inc\tx_ghs.h +..\src\tx_el.c +..\src\tx_ghs.c +..\src\tx_ghse.c +..\src\tx_thread_context_restore.arm +..\src\tx_thread_context_save.arm +..\src\tx_thread_fiq_context_restore.arm +..\src\tx_thread_fiq_context_save.arm +..\src\tx_thread_fiq_nesting_end.arm +..\src\tx_thread_fiq_nesting_start.arm +..\src\tx_thread_interrupt_control.arm +..\src\tx_thread_interrupt_disable.arm +..\src\tx_thread_interrupt_restore.arm +..\src\tx_thread_irq_nesting_end.arm +..\src\tx_thread_irq_nesting_start.arm +..\src\tx_thread_schedule.arm +..\src\tx_thread_stack_build.arm +..\src\tx_thread_system_return.arm +..\src\tx_thread_vectored_context_save.arm +..\src\tx_timer_interrupt.arm +..\..\..\..\common\src\tx_block_allocate.c +..\..\..\..\common\src\tx_block_pool_cleanup.c +..\..\..\..\common\src\tx_block_pool_create.c +..\..\..\..\common\src\tx_block_pool_delete.c +..\..\..\..\common\src\tx_block_pool_info_get.c +..\..\..\..\common\src\tx_block_pool_initialize.c +..\..\..\..\common\src\tx_block_pool_performance_info_get.c +..\..\..\..\common\src\tx_block_pool_performance_system_info_get.c +..\..\..\..\common\src\tx_block_pool_prioritize.c +..\..\..\..\common\src\tx_block_release.c +..\..\..\..\common\src\tx_byte_allocate.c +..\..\..\..\common\src\tx_byte_pool_cleanup.c +..\..\..\..\common\src\tx_byte_pool_create.c +..\..\..\..\common\src\tx_byte_pool_delete.c +..\..\..\..\common\src\tx_byte_pool_info_get.c +..\..\..\..\common\src\tx_byte_pool_initialize.c +..\..\..\..\common\src\tx_byte_pool_performance_info_get.c +..\..\..\..\common\src\tx_byte_pool_performance_system_info_get.c +..\..\..\..\common\src\tx_byte_pool_prioritize.c +..\..\..\..\common\src\tx_byte_pool_search.c +..\..\..\..\common\src\tx_byte_release.c +..\..\..\..\common\src\tx_event_flags_cleanup.c +..\..\..\..\common\src\tx_event_flags_create.c +..\..\..\..\common\src\tx_event_flags_delete.c +..\..\..\..\common\src\tx_event_flags_get.c +..\..\..\..\common\src\tx_event_flags_info_get.c +..\..\..\..\common\src\tx_event_flags_initialize.c +..\..\..\..\common\src\tx_event_flags_performance_info_get.c +..\..\..\..\common\src\tx_event_flags_performance_system_info_get.c +..\..\..\..\common\src\tx_event_flags_set.c +..\..\..\..\common\src\tx_event_flags_set_notify.c +..\..\..\..\common\src\tx_initialize_high_level.c +..\..\..\..\common\src\tx_initialize_kernel_enter.c +..\..\..\..\common\src\tx_initialize_kernel_setup.c +..\..\..\..\common\src\tx_mutex_cleanup.c +..\..\..\..\common\src\tx_mutex_create.c +..\..\..\..\common\src\tx_mutex_delete.c +..\..\..\..\common\src\tx_mutex_get.c +..\..\..\..\common\src\tx_mutex_info_get.c +..\..\..\..\common\src\tx_mutex_initialize.c +..\..\..\..\common\src\tx_mutex_performance_info_get.c +..\..\..\..\common\src\tx_mutex_performance_system_info_get.c +..\..\..\..\common\src\tx_mutex_prioritize.c +..\..\..\..\common\src\tx_mutex_priority_change.c +..\..\..\..\common\src\tx_mutex_put.c +..\..\..\..\common\src\tx_queue_cleanup.c +..\..\..\..\common\src\tx_queue_create.c +..\..\..\..\common\src\tx_queue_delete.c +..\..\..\..\common\src\tx_queue_flush.c +..\..\..\..\common\src\tx_queue_front_send.c +..\..\..\..\common\src\tx_queue_info_get.c +..\..\..\..\common\src\tx_queue_initialize.c +..\..\..\..\common\src\tx_queue_performance_info_get.c +..\..\..\..\common\src\tx_queue_performance_system_info_get.c +..\..\..\..\common\src\tx_queue_prioritize.c +..\..\..\..\common\src\tx_queue_receive.c +..\..\..\..\common\src\tx_queue_send.c +..\..\..\..\common\src\tx_queue_send_notify.c +..\..\..\..\common\src\tx_semaphore_ceiling_put.c +..\..\..\..\common\src\tx_semaphore_cleanup.c +..\..\..\..\common\src\tx_semaphore_create.c +..\..\..\..\common\src\tx_semaphore_delete.c +..\..\..\..\common\src\tx_semaphore_get.c +..\..\..\..\common\src\tx_semaphore_info_get.c +..\..\..\..\common\src\tx_semaphore_initialize.c +..\..\..\..\common\src\tx_semaphore_performance_info_get.c +..\..\..\..\common\src\tx_semaphore_performance_system_info_get.c +..\..\..\..\common\src\tx_semaphore_prioritize.c +..\..\..\..\common\src\tx_semaphore_put.c +..\..\..\..\common\src\tx_semaphore_put_notify.c +..\..\..\..\common\src\tx_thread_create.c +..\..\..\..\common\src\tx_thread_delete.c +..\..\..\..\common\src\tx_thread_entry_exit_notify.c +..\..\..\..\common\src\tx_thread_identify.c +..\..\..\..\common\src\tx_thread_info_get.c +..\..\..\..\common\src\tx_thread_initialize.c +..\..\..\..\common\src\tx_thread_performance_info_get.c +..\..\..\..\common\src\tx_thread_performance_system_info_get.c +..\..\..\..\common\src\tx_thread_preemption_change.c +..\..\..\..\common\src\tx_thread_priority_change.c +..\..\..\..\common\src\tx_thread_relinquish.c +..\..\..\..\common\src\tx_thread_reset.c +..\..\..\..\common\src\tx_thread_resume.c +..\..\..\..\common\src\tx_thread_shell_entry.c +..\..\..\..\common\src\tx_thread_sleep.c +..\..\..\..\common\src\tx_thread_stack_analyze.c +..\..\..\..\common\src\tx_thread_stack_error_handler.c +..\..\..\..\common\src\tx_thread_stack_error_notify.c +..\..\..\..\common\src\tx_thread_suspend.c +..\..\..\..\common\src\tx_thread_system_preempt_check.c +..\..\..\..\common\src\tx_thread_system_resume.c +..\..\..\..\common\src\tx_thread_system_suspend.c +..\..\..\..\common\src\tx_thread_terminate.c +..\..\..\..\common\src\tx_thread_time_slice.c +..\..\..\..\common\src\tx_thread_time_slice_change.c +..\..\..\..\common\src\tx_thread_timeout.c +..\..\..\..\common\src\tx_thread_wait_abort.c +..\..\..\..\common\src\tx_time_get.c +..\..\..\..\common\src\tx_time_set.c +..\..\..\..\common\src\tx_timer_activate.c +..\..\..\..\common\src\tx_timer_change.c +..\..\..\..\common\src\tx_timer_create.c +..\..\..\..\common\src\tx_timer_deactivate.c +..\..\..\..\common\src\tx_timer_delete.c +..\..\..\..\common\src\tx_timer_expiration_process.c +..\..\..\..\common\src\tx_timer_info_get.c +..\..\..\..\common\src\tx_timer_initialize.c +..\..\..\..\common\src\tx_timer_performance_info_get.c +..\..\..\..\common\src\tx_timer_performance_system_info_get.c +..\..\..\..\common\src\tx_timer_system_activate.c +..\..\..\..\common\src\tx_timer_system_deactivate.c +..\..\..\..\common\src\tx_timer_thread_entry.c +..\..\..\..\common\src\tx_trace_buffer_full_notify.c +..\..\..\..\common\src\tx_trace_disable.c +..\..\..\..\common\src\tx_trace_enable.c +..\..\..\..\common\src\tx_trace_event_filter.c +..\..\..\..\common\src\tx_trace_event_unfilter.c +..\..\..\..\common\src\tx_trace_initialize.c +..\..\..\..\common\src\tx_trace_interrupt_control.c +..\..\..\..\common\src\tx_trace_isr_enter_insert.c +..\..\..\..\common\src\tx_trace_isr_exit_insert.c +..\..\..\..\common\src\tx_trace_object_register.c +..\..\..\..\common\src\tx_trace_object_unregister.c +..\..\..\..\common\src\tx_trace_user_event_insert.c +..\..\..\..\common\src\txe_block_allocate.c +..\..\..\..\common\src\txe_block_pool_create.c +..\..\..\..\common\src\txe_block_pool_delete.c +..\..\..\..\common\src\txe_block_pool_info_get.c +..\..\..\..\common\src\txe_block_pool_prioritize.c +..\..\..\..\common\src\txe_block_release.c +..\..\..\..\common\src\txe_byte_allocate.c +..\..\..\..\common\src\txe_byte_pool_create.c +..\..\..\..\common\src\txe_byte_pool_delete.c +..\..\..\..\common\src\txe_byte_pool_info_get.c +..\..\..\..\common\src\txe_byte_pool_prioritize.c +..\..\..\..\common\src\txe_byte_release.c +..\..\..\..\common\src\txe_event_flags_create.c +..\..\..\..\common\src\txe_event_flags_delete.c +..\..\..\..\common\src\txe_event_flags_get.c +..\..\..\..\common\src\txe_event_flags_info_get.c +..\..\..\..\common\src\txe_event_flags_set.c +..\..\..\..\common\src\txe_event_flags_set_notify.c +..\..\..\..\common\src\txe_mutex_create.c +..\..\..\..\common\src\txe_mutex_delete.c +..\..\..\..\common\src\txe_mutex_get.c +..\..\..\..\common\src\txe_mutex_info_get.c +..\..\..\..\common\src\txe_mutex_prioritize.c +..\..\..\..\common\src\txe_mutex_put.c +..\..\..\..\common\src\txe_queue_create.c +..\..\..\..\common\src\txe_queue_delete.c +..\..\..\..\common\src\txe_queue_flush.c +..\..\..\..\common\src\txe_queue_front_send.c +..\..\..\..\common\src\txe_queue_info_get.c +..\..\..\..\common\src\txe_queue_prioritize.c +..\..\..\..\common\src\txe_queue_receive.c +..\..\..\..\common\src\txe_queue_send.c +..\..\..\..\common\src\txe_queue_send_notify.c +..\..\..\..\common\src\txe_semaphore_ceiling_put.c +..\..\..\..\common\src\txe_semaphore_create.c +..\..\..\..\common\src\txe_semaphore_delete.c +..\..\..\..\common\src\txe_semaphore_get.c +..\..\..\..\common\src\txe_semaphore_info_get.c +..\..\..\..\common\src\txe_semaphore_prioritize.c +..\..\..\..\common\src\txe_semaphore_put.c +..\..\..\..\common\src\txe_semaphore_put_notify.c +..\..\..\..\common\src\txe_thread_create.c +..\..\..\..\common\src\txe_thread_delete.c +..\..\..\..\common\src\txe_thread_entry_exit_notify.c +..\..\..\..\common\src\txe_thread_info_get.c +..\..\..\..\common\src\txe_thread_preemption_change.c +..\..\..\..\common\src\txe_thread_priority_change.c +..\..\..\..\common\src\txe_thread_relinquish.c +..\..\..\..\common\src\txe_thread_reset.c +..\..\..\..\common\src\txe_thread_resume.c +..\..\..\..\common\src\txe_thread_suspend.c +..\..\..\..\common\src\txe_thread_terminate.c +..\..\..\..\common\src\txe_thread_time_slice_change.c +..\..\..\..\common\src\txe_thread_wait_abort.c +..\..\..\..\common\src\txe_timer_activate.c +..\..\..\..\common\src\txe_timer_change.c +..\..\..\..\common\src\txe_timer_create.c +..\..\..\..\common\src\txe_timer_deactivate.c +..\..\..\..\common\src\txe_timer_delete.c +..\..\..\..\common\src\txe_timer_info_get.c diff --git a/ports/cortex_r4/ghs/inc/tx_el.h b/ports/cortex_r4/ghs/inc/tx_el.h new file mode 100644 index 00000000..66cc0d7c --- /dev/null +++ b/ports/cortex_r4/ghs/inc/tx_el.h @@ -0,0 +1,765 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* COMPONENT DEFINITION RELEASE */ +/* */ +/* tx_el.h PORTABLE C/GHS */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the ThreadX event log functions for the GHS MULTI */ +/* EventAnalyzer. It is assumed that tx_api.h and tx_port.h have */ +/* already been included. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ + +#ifndef TX_EL_H +#define TX_EL_H + + +/* Define Event Log specific data definitions. */ + +#define TX_EL_VERSION_ID 2 /* Event log version ID */ +#define TX_EL_HEADER_SIZE 24 /* Event log header size */ +#define TX_EL_TNIS 16 /* Number of thread names supported */ + /* If the application needs to */ + /* track more thread names, just */ + /* increase this number and re- */ + /* build the ThreadX library. */ +#define TX_EL_TNI_ENTRY_SIZE 44 /* Thread name entries are 44 bytes */ +#define TX_EL_TNI_NAME_SIZE 34 /* Thread name size in TNI */ +#define TX_EL_NO_MORE_TNI_ROOM 1 /* Error return from thread register*/ +#define TX_EL_NAME_NOT_FOUND 2 /* Error return from un-register */ +#define TX_EL_EVENT_SIZE 32 /* Number of bytes in each event */ +#define TX_EL_VALID_ENTRY 1 /* Valid log entry */ +#define TX_EL_INVALID_ENTRY 0 /* Invalid log entry */ + + +/* Define necessary offsets. */ + +#define TX_EL_TNI_VALID_OFFSET 34 +#define TX_EL_TNI_THREAD_ID_OFFSET 36 +#define TX_EL_TNI_THREAD_PRIORITY_OFF 40 +#define TX_EL_EVENT_TYPE_OFFSET 0 +#define TX_EL_EVENT_SUBTYPE_OFFSET 2 +#define TX_EL_EVENT_TIME_UPPER_OFFSET 4 +#define TX_EL_EVENT_TIME_LOWER_OFFSET 8 +#define TX_EL_EVENT_THREAD_OFFSET 12 +#define TX_EL_EVENT_INFO_1_OFFSET 16 +#define TX_EL_EVENT_INFO_2_OFFSET 20 +#define TX_EL_EVENT_INFO_3_OFFSET 24 +#define TX_EL_EVENT_INFO_4_OFFSET 28 + + +/* Undefine constants that might be been defined previously by tx_api.h. */ + +#undef TX_EL_INITIALIZE +#undef TX_EL_THREAD_REGISTER +#undef TX_EL_THREAD_UNREGISTER +#undef TX_EL_THREAD_STATUS_CHANGE_INSERT +#undef TX_EL_BYTE_ALLOCATE_INSERT +#undef TX_EL_BYTE_POOL_CREATE_INSERT +#undef TX_EL_BYTE_POOL_DELETE_INSERT +#undef TX_EL_BYTE_RELEASE_INSERT +#undef TX_EL_BLOCK_ALLOCATE_INSERT +#undef TX_EL_BLOCK_POOL_CREATE_INSERT +#undef TX_EL_BLOCK_POOL_DELETE_INSERT +#undef TX_EL_BLOCK_RELEASE_INSERT +#undef TX_EL_EVENT_FLAGS_CREATE_INSERT +#undef TX_EL_EVENT_FLAGS_DELETE_INSERT +#undef TX_EL_EVENT_FLAGS_GET_INSERT +#undef TX_EL_EVENT_FLAGS_SET_INSERT +#undef TX_EL_INTERRUPT_CONTROL_INSERT +#undef TX_EL_QUEUE_CREATE_INSERT +#undef TX_EL_QUEUE_DELETE_INSERT +#undef TX_EL_QUEUE_FLUSH_INSERT +#undef TX_EL_QUEUE_RECEIVE_INSERT +#undef TX_EL_QUEUE_SEND_INSERT +#undef TX_EL_SEMAPHORE_CREATE_INSERT +#undef TX_EL_SEMAPHORE_DELETE_INSERT +#undef TX_EL_SEMAPHORE_GET_INSERT +#undef TX_EL_SEMAPHORE_PUT_INSERT +#undef TX_EL_THREAD_CREATE_INSERT +#undef TX_EL_THREAD_DELETE_INSERT +#undef TX_EL_THREAD_IDENTIFY_INSERT +#undef TX_EL_THREAD_PREEMPTION_CHANGE_INSERT +#undef TX_EL_THREAD_PRIORITY_CHANGE_INSERT +#undef TX_EL_THREAD_RELINQUISH_INSERT +#undef TX_EL_THREAD_RESUME_INSERT +#undef TX_EL_THREAD_SLEEP_INSERT +#undef TX_EL_THREAD_SUSPEND_INSERT +#undef TX_EL_THREAD_TERMINATE_INSERT +#undef TX_EL_THREAD_TIME_SLICE_CHANGE_INSERT +#undef TX_EL_TIME_GET_INSERT +#undef TX_EL_TIME_SET_INSERT +#undef TX_EL_TIMER_ACTIVATE_INSERT +#undef TX_EL_TIMER_CHANGE_INSERT +#undef TX_EL_TIMER_CREATE_INSERT +#undef TX_EL_TIMER_DEACTIVATE_INSERT +#undef TX_EL_TIMER_DELETE_INSERT +#undef TX_EL_BLOCK_POOL_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PRIORITIZE_INSERT +#undef TX_EL_BYTE_POOL_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PRIORITIZE_INSERT +#undef TX_EL_EVENT_FLAGS_INFO_GET_INSERT +#undef TX_EL_MUTEX_CREATE_INSERT +#undef TX_EL_MUTEX_DELETE_INSERT +#undef TX_EL_MUTEX_GET_INSERT +#undef TX_EL_MUTEX_INFO_GET_INSERT +#undef TX_EL_MUTEX_PRIORITIZE_INSERT +#undef TX_EL_MUTEX_PUT_INSERT +#undef TX_EL_QUEUE_INFO_GET_INSERT +#undef TX_EL_QUEUE_FRONT_SEND_INSERT +#undef TX_EL_QUEUE_PRIORITIZE_INSERT +#undef TX_EL_SEMAPHORE_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PRIORITIZE_INSERT +#undef TX_EL_THREAD_INFO_GET_INSERT +#undef TX_EL_THREAD_WAIT_ABORT_INSERT +#undef TX_EL_TIMER_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT +#undef TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_QUEUE_SEND_NOTIFY_INSERT +#undef TX_EL_SEMAPHORE_CEILING_PUT_INSERT +#undef TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT +#undef TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT +#undef TX_EL_THREAD_RESET_INSERT +#undef TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT +#undef TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT +#undef TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT +#undef TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT + + +/* Define Event Types. */ + +#define TX_EL_THREAD_CHANGE 1 +#define TX_EL_INTERRUPT 2 +#define TX_EL_THREADX_CALL 3 +#define TX_EL_USER_EVENT 4 +#define TX_EL_THREAD_STATUS_CHANGE 5 +#define TX_EL_REFRESH 6 /* Not implemented */ +#define TX_EL_TIMER 7 /* Not implemented */ +#define TX_EL_TIMESOURCE_DELTA 8 /* Not implemented */ + + +/* Define TX_EL_THREADX_CALL event sub-types. */ + +#define TX_EL_BYTE_ALLOCATE 0 +#define TX_EL_BYTE_POOL_CREATE 1 +#define TX_EL_BYTE_POOL_DELETE 2 +#define TX_EL_BYTE_RELEASE 3 +#define TX_EL_BLOCK_ALLOCATE 4 +#define TX_EL_BLOCK_POOL_CREATE 5 +#define TX_EL_BLOCK_POOL_DELETE 6 +#define TX_EL_BLOCK_RELEASE 7 +#define TX_EL_EVENT_FLAGS_CREATE 8 +#define TX_EL_EVENT_FLAGS_DELETE 9 +#define TX_EL_EVENT_FLAGS_GET 10 +#define TX_EL_EVENT_FLAGS_SET 11 +#define TX_EL_INTERRUPT_CONTROL 12 +#define TX_EL_QUEUE_CREATE 13 +#define TX_EL_QUEUE_DELETE 14 +#define TX_EL_QUEUE_FLUSH 15 +#define TX_EL_QUEUE_RECEIVE 16 +#define TX_EL_QUEUE_SEND 17 +#define TX_EL_SEMAPHORE_CREATE 18 +#define TX_EL_SEMAPHORE_DELETE 19 +#define TX_EL_SEMAPHORE_GET 20 +#define TX_EL_SEMAPHORE_PUT 21 +#define TX_EL_THREAD_CREATE 22 +#define TX_EL_THREAD_DELETE 23 +#define TX_EL_THREAD_IDENTIFY 24 +#define TX_EL_THREAD_PREEMPTION_CHANGE 25 +#define TX_EL_THREAD_PRIORITY_CHANGE 26 +#define TX_EL_THREAD_RELINQUISH 27 +#define TX_EL_THREAD_RESUME 28 +#define TX_EL_THREAD_SLEEP 29 +#define TX_EL_THREAD_SUSPEND 30 +#define TX_EL_THREAD_TERMINATE 31 +#define TX_EL_THREAD_TIME_SLICE_CHANGE 32 +#define TX_EL_TIME_GET 33 +#define TX_EL_TIME_SET 34 +#define TX_EL_TIMER_ACTIVATE 35 +#define TX_EL_TIMER_CHANGE 36 +#define TX_EL_TIMER_CREATE 37 +#define TX_EL_TIMER_DEACTIVATE 38 +#define TX_EL_TIMER_DELETE 39 +#define TX_EL_BLOCK_POOL_INFO_GET 40 +#define TX_EL_BLOCK_POOL_PRIORITIZE 41 +#define TX_EL_BYTE_POOL_INFO_GET 42 +#define TX_EL_BYTE_POOL_PRIORITIZE 43 +#define TX_EL_EVENT_FLAGS_INFO_GET 44 +#define TX_EL_MUTEX_CREATE 45 +#define TX_EL_MUTEX_DELETE 46 +#define TX_EL_MUTEX_GET 47 +#define TX_EL_MUTEX_INFO_GET 48 +#define TX_EL_MUTEX_PRIORITIZE 49 +#define TX_EL_MUTEX_PUT 50 +#define TX_EL_QUEUE_INFO_GET 51 +#define TX_EL_QUEUE_FRONT_SEND 52 +#define TX_EL_QUEUE_PRIORITIZE 53 +#define TX_EL_SEMAPHORE_INFO_GET 54 +#define TX_EL_SEMAPHORE_PRIORITIZE 55 +#define TX_EL_THREAD_INFO_GET 56 +#define TX_EL_THREAD_WAIT_ABORT 57 +#define TX_EL_TIMER_INFO_GET 58 +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET 59 +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET 60 +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET 61 +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET 62 +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET 63 +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET 64 +#define TX_EL_EVENT_FLAGS_SET_NOTIFY 65 +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET 66 +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET 67 +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET 68 +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET 69 +#define TX_EL_QUEUE_SEND_NOTIFY 70 +#define TX_EL_SEMAPHORE_CEILING_PUT 71 +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET 72 +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET 73 +#define TX_EL_SEMAPHORE_PUT_NOTIFY 74 +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY 75 +#define TX_EL_THREAD_RESET 76 +#define TX_EL_THREAD_PERFORMANCE_INFO_GET 77 +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET 78 +#define TX_EL_THREAD_STACK_ERROR_NOTIFY 79 +#define TX_EL_TIMER_PERFORMANCE_INFO_GET 80 +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET 81 + + +/* Define ThreadX sub-types. */ + +#define TX_EL_INTERRUPT_SUB_TYPE 1 +#define TX_EL_END_OF_INTERRUPT 3 + + +/* Define event logging filters, which may be used by the application program to + dynamically enable/disable events in run-time. */ + +#define TX_EL_FILTER_STATUS_CHANGE 0x0001 +#define TX_EL_FILTER_INTERRUPTS 0x0002 +#define TX_EL_FILTER_THREAD_CALLS 0x0004 +#define TX_EL_FILTER_TIMER_CALLS 0x0008 +#define TX_EL_FILTER_EVENT_FLAG_CALLS 0x0010 +#define TX_EL_FILTER_SEMAPHORE_CALLS 0x0020 +#define TX_EL_FILTER_QUEUE_CALLS 0x0040 +#define TX_EL_FILTER_BLOCK_CALLS 0x0080 +#define TX_EL_FILTER_BYTE_CALLS 0x0100 +#define TX_EL_FILTER_MUTEX_CALLS 0x0200 +#define TX_EL_FILTER_ALL_EVENTS 0xFFFF +#define TX_EL_ENABLE_ALL_EVENTS 0x0000 + + +/* Define filter macros that are inserted in-line with the other macros below. */ + +#ifdef TX_ENABLE_EVENT_FILTERS +#define TX_EL_NO_STATUS_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_STATUS_CHANGE)) { +#define TX_EL_NO_INTERRUPT_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_INTERRUPTS)) { +#define TX_EL_NO_THREAD_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_THREAD_CALLS)) { +#define TX_EL_NO_TIMER_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_TIMER_CALLS)) { +#define TX_EL_NO_EVENT_FLAG_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_EVENT_FLAG_CALLS)) { +#define TX_EL_NO_SEMAPHORE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_SEMAPHORE_CALLS)) { +#define TX_EL_NO_QUEUE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_QUEUE_CALLS)) { +#define TX_EL_NO_BLOCK_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_BLOCK_CALLS)) { +#define TX_EL_NO_BYTE_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_BYTE_CALLS)) { +#define TX_EL_NO_MUTEX_EVENTS if (!(_tx_el_event_filter & TX_EL_FILTER_MUTEX_CALLS)) { +#define TX_EL_END_FILTER } +#else +#define TX_EL_NO_STATUS_EVENTS +#define TX_EL_NO_INTERRUPT_EVENTS +#define TX_EL_NO_THREAD_EVENTS +#define TX_EL_NO_TIMER_EVENTS +#define TX_EL_NO_EVENT_FLAG_EVENTS +#define TX_EL_NO_SEMAPHORE_EVENTS +#define TX_EL_NO_QUEUE_EVENTS +#define TX_EL_NO_BLOCK_EVENTS +#define TX_EL_NO_BYTE_EVENTS +#define TX_EL_NO_MUTEX_EVENTS +#define TX_EL_END_FILTER +#endif + +/* Define externs and constants for non-event log source modules. This is for + the in-line macros below. */ + +#ifndef TX_EL_SOURCE_CODE +extern UCHAR *_tx_el_tni_start; +extern UCHAR **_tx_el_current_event; +extern UCHAR *_tx_el_event_area_start; +extern UCHAR *_tx_el_event_area_end; +extern UINT _tx_el_maximum_events; +extern ULONG _tx_el_total_events; +extern TX_THREAD *_tx_thread_current_ptr; +extern UINT _tx_el_event_filter; +extern ULONG _tx_el_time_base_upper; +extern ULONG _tx_el_time_base_lower; + + +/* Define macros for event logging functions. */ + +#define TX_EL_THREAD_CREATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_THREAD_CREATE, thread_ptr, stack_start, stack_size, priority); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_SET, group_ptr, flags_to_set, set_option); TX_EL_END_FILTER +#define TX_EL_THREAD_DELETE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_DELETE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_TIME_SLICE_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_TIME_SLICE_CHANGE, thread_ptr, thread_ptr -> tx_thread_new_time_slice, new_time_slice); TX_EL_END_FILTER +#define TX_EL_THREAD_TERMINATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_TERMINATE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_SLEEP_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SLEEP, timer_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_SUSPEND_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SUSPEND, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_RELINQUISH_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_RELINQUISH); TX_EL_END_FILTER +#define TX_EL_THREAD_RESUME_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESUME, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PRIORITY_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PRIORITY_CHANGE, thread_ptr, thread_ptr -> tx_thread_priority, new_priority); TX_EL_END_FILTER +#define TX_EL_THREAD_PREEMPTION_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PREEMPTION_CHANGE, thread_ptr, thread_ptr -> tx_thread_preempt_threshold, new_threshold); TX_EL_END_FILTER +#define TX_EL_THREAD_WAIT_ABORT_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_WAIT_ABORT, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_THREAD_ENTRY_EXIT_NOTIFY, thread_ptr, thread_entry_exit_notify); TX_EL_END_FILTER +#define TX_EL_THREAD_RESET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_PERFORMANCE_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_STACK_ERROR_NOTIFY, stack_error_handler); TX_EL_END_FILTER +#define TX_EL_TIME_SET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_SET, new_time); TX_EL_END_FILTER +#define TX_EL_TIME_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_GET, _tx_timer_system_clock); TX_EL_END_FILTER +#define TX_EL_TIMER_DELETE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DELETE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_CREATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate); TX_EL_END_FILTER +#define TX_EL_TIMER_CHANGE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_TIMER_CHANGE, timer_ptr, initial_ticks, reschedule_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_IDENTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_IDENTIFY); TX_EL_END_FILTER +#define TX_EL_TIMER_DEACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DEACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_ACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_ACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_PERFORMANCE_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_GET, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_DELETE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_DELETE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CREATE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_CREATE, semaphore_ptr, initial_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PRIORITIZE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PRIORITIZE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CEILING_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_SEMAPHORE_CEILING_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, ceiling); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT_NOTIFY, semaphore_ptr, semaphore_put_notify); TX_EL_END_FILTER +#define TX_EL_QUEUE_FRONT_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_FRONT_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_RECEIVE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_RECEIVE, queue_ptr, destination_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_FLUSH_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_FLUSH, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_DELETE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_DELETE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_CREATE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_QUEUE_CREATE, queue_ptr, queue_start, queue_size, message_size); TX_EL_END_FILTER +#define TX_EL_QUEUE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PRIORITIZE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PRIORITIZE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PERFORMANCE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_NOTIFY_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND_NOTIFY, queue_ptr, queue_send_notify); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_GET, group_ptr, requested_flags, get_option); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_DELETE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_DELETE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_CREATE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_CREATE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_EVENT_FLAGS_SET_NOTIFY, group_ptr, events_set_notify); TX_EL_END_FILTER +#define TX_EL_BYTE_RELEASE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BYTE_RELEASE, pool_ptr, memory_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_DELETE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_CREATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_POOL_CREATE, pool_ptr, pool_start, pool_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PRIORITIZE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_ALLOCATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_ALLOCATE, pool_ptr, memory_ptr, memory_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_BLOCK_RELEASE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_RELEASE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_DELETE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_CREATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_BLOCK_POOL_CREATE, pool_ptr, pool_start, pool_size, block_size); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PRIORITIZE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_ALLOCATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_ALLOCATE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_MUTEX_CREATE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_MUTEX_CREATE, mutex_ptr, inherit); TX_EL_END_FILTER +#define TX_EL_MUTEX_DELETE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_DELETE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_GET, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PRIORITIZE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PRIORITIZE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PUT_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_PUT, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PERFORMANCE_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER + + +#endif + + +/* Define Event Log function prototypes. */ + +VOID _tx_el_initialize(VOID); +UINT _tx_el_thread_register(TX_THREAD *thread_ptr); +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr); +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4); +VOID _tx_el_thread_running(TX_THREAD *thread_ptr); +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr); +VOID _tx_el_interrupt(UINT interrupt_number); +VOID _tx_el_interrupt_end(UINT interrupt_number); +VOID _tx_el_interrupt_control_call(void); +VOID _tx_el_event_log_on(void); +VOID _tx_el_event_log_off(void); +VOID _tx_el_event_filter_set(UINT filter); + + +/* Define macros that are used inside the ThreadX source code. + If event logging is disabled, these macros will be defined + as white space. */ + +#ifdef TX_ENABLE_EVENT_LOGGING +#ifndef TX_NO_EVENT_INFO +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =\ + (ULONG) e;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#endif +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) +#define TX_EL_THREAD_REGISTER(a) +#define TX_EL_THREAD_UNREGISTER(a) +#define TX_EL_INITIALIZE +#endif + +#endif + diff --git a/ports/cortex_r4/ghs/inc/tx_ghs.h b/ports/cortex_r4/ghs/inc/tx_ghs.h new file mode 100644 index 00000000..ca976916 --- /dev/null +++ b/ports/cortex_r4/ghs/inc/tx_ghs.h @@ -0,0 +1,77 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#ifndef _TX_GHS_H_ +#define _TX_GHS_H_ + +#include +#include +#include +#include + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +extern void *__ghs_GetThreadLocalStorageItem(int specifier); + +/* Thread-local storage routines for Green Hills releases 5.x and beyond. + The following specifiers are used when calling + __ghs_GetThreadLocalStorageItem. + + If __ghs_GetThreadLocalStorageItem is customized to + return a per-thread errno value, define the preprocessor symbol + USE_THREAD_LOCAL_ERRNO in ind_errn.c. + */ + +enum __ghs_ThreadLocalStorage_specifier { + __ghs_TLS_asctime_buff, + __ghs_TLS_tmpnam_space, + __ghs_TLS_strtok_saved_pos, + __ghs_TLS_Errno, + __ghs_TLS_gmtime_temp, + __ghs_TLS___eh_globals, + __ghs_TLS_SignalHandlers +}; +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ +typedef void (*SignalHandler)(int); + +typedef struct +{ + int Errno; /* errno. */ + SignalHandler SignalHandlers[_SIGMAX]; /* signal() buffer. */ + char tmpnam_space[L_tmpnam]; /* tmpnam(NULL) buffer. */ + char asctime_buff[30]; /* . */ + char *strtok_saved_pos; /* strtok() position. */ + struct tm gmtime_temp; /* gmtime() and localtime() buffer. */ + void *__eh_globals; /* Pointer for C++ exception handling. */ +} ThreadLocalStorage; + +ThreadLocalStorage *GetThreadLocalStorage(void); +#endif + + +void __ghsLock(void); +void __ghsUnlock(void); + +int __ghs_SaveSignalContext(jmp_buf); +void __ghs_RestoreSignalContext(jmp_buf); + +/* prototypes for FILE lock routines. */ +void __ghs_flock_file(void *); +void __ghs_funlock_file(void *); +int __ghs_ftrylock_file(void *); +void __ghs_flock_create(void **); +void __ghs_flock_destroy(void *); + +/* prototype for GHS/ThreadX error shell checking. */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal); + +#endif /* _TX_GHS_H_ */ diff --git a/ports/cortex_r4/green/inc/tx_port.h b/ports/cortex_r4/ghs/inc/tx_port.h similarity index 92% rename from ports/cortex_r4/green/inc/tx_port.h rename to ports/cortex_r4/ghs/inc/tx_port.h index e1524f7a..ab32b974 100644 --- a/ports/cortex_r4/green/inc/tx_port.h +++ b/ports/cortex_r4/ghs/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-R4/Green Hills */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-R4/GHS */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -78,7 +78,7 @@ #include "tx_ghs.h" -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -114,12 +114,12 @@ typedef unsigned short USHORT; #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #ifdef TX_ENABLE_FIQ_SUPPORT #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ @@ -134,13 +134,13 @@ typedef unsigned short USHORT; /* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ -#define TX_EL_TICKS_PER_SECOND 1000000 +#define TX_EL_TICKS_PER_SECOND 1000000 /* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply - simulate the time-stamp source with a counter. */ + simulate the time-stamp source with a counter. */ -#define read_tbu() _tx_el_time_base_upper -#define read_tbl() ++_tx_el_time_base_lower +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower /* Define the port specific options for the _tx_build_options variable. This variable indicates @@ -174,7 +174,7 @@ typedef unsigned short USHORT; #define TX_INLINE_INITIALIZATION -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -186,10 +186,10 @@ typedef unsigned short USHORT; /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_0 #define TX_THREAD_EXTENSION_1 VOID * tx_thread_eh_globals; #define TX_THREAD_EXTENSION_2 int Errno; /* errno. */ #define TX_THREAD_EXTENSION_3 char * strtok_saved_pos; /* strtok() position. */ @@ -206,11 +206,11 @@ typedef unsigned short USHORT; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -240,7 +240,7 @@ typedef unsigned short USHORT; extern void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr); \ __tx_cpp_exception_cleanup(thread_ptr); \ } -#else +#else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) \ { \ #pragma weak __cpp_exception_cleanup \ @@ -278,18 +278,18 @@ typedef unsigned short USHORT; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) m = m & ((ULONG) (-((LONG) m))); \ b = __CLZ32(m); \ - b = 31 - b; + b = 31 - b; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -299,7 +299,7 @@ typedef unsigned short USHORT; unsigned int _tx_thread_interrupt_disable(void); void _tx_thread_interrupt_restore(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); @@ -307,7 +307,7 @@ void _tx_thread_interrupt_restore(unsigned int new_po #else -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER >= 350) @@ -346,7 +346,7 @@ asm int disable_ints(void) MSR CPSR_c,r1 #else #ifdef TX_ENABLE_FIQ_SUPPORT - CPSID if + CPSID if #else CPSID i #endif @@ -385,7 +385,7 @@ asm void restore_ints(int a) #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-R4/Green Hills Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-R4/Green Hills Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports/cortex_r4/green/readme_threadx.txt b/ports/cortex_r4/ghs/readme_threadx.txt similarity index 100% rename from ports/cortex_r4/green/readme_threadx.txt rename to ports/cortex_r4/ghs/readme_threadx.txt diff --git a/ports/cortex_r4/ghs/src/tx_el.c b/ports/cortex_r4/ghs/src/tx_el.c new file mode 100644 index 00000000..d8f056d7 --- /dev/null +++ b/ports/cortex_r4/ghs/src/tx_el.c @@ -0,0 +1,1165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_EL_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_el.h" +#include "string.h" + + +/* Define global variables used to manage the event pool. */ + +UCHAR *_tx_el_tni_start; +UCHAR **_tx_el_current_event; +UCHAR *_tx_el_event_area_start; +UCHAR *_tx_el_event_area_end; +UINT _tx_el_maximum_events; +ULONG _tx_el_total_events; +UINT _tx_el_event_filter; +ULONG _tx_el_time_base_upper; +ULONG _tx_el_time_base_lower; + +extern char __ghsbegin_eventlog[]; +extern char __ghsend_eventlog[]; + +extern TX_THREAD *_tx_thread_current_ptr; +UINT _tx_thread_interrupt_control(UINT new_posture); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the Event Log (in the format dictated by the */ +/* GHS Event Analyzer) and sets up various information for subsequent */ +/* operation. The start and end of the Event Log is determined by the */ +/* .eventlog section in the linker control file. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_initialize(VOID) +{ + +UCHAR *work_ptr; +UCHAR *read_ptr; +ULONG event_log_size; +UCHAR *end_ptr; +UINT i; + + + /* Clear total event counter. */ + _tx_el_total_events = 0; + + /* Clear event filter. */ + _tx_el_event_filter = 0; + + /* First, pickup the starting and ending address of the Event Log memory. */ + work_ptr = (unsigned char *) __ghsbegin_eventlog; + end_ptr = (unsigned char *) __ghsend_eventlog; + + /* Calculate the event log size. */ + event_log_size = end_ptr - work_ptr; + + /* Subtract off the number of bytes in the header and the TNI area. */ + event_log_size = event_log_size - (TX_EL_HEADER_SIZE + + (TX_EL_TNI_ENTRY_SIZE * TX_EL_TNIS)); + + /* Make sure the event log is evenly divisible by the event size. */ + event_log_size = (event_log_size/TX_EL_EVENT_SIZE) * TX_EL_EVENT_SIZE; + + /* Build the Event Log header. */ + + /* Setup the Event Log Version ID. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_VERSION_ID; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the TNIS (number of thread names) field. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_TNIS; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the EVPS (event pool size) field. */ + *((ULONG *) work_ptr) = event_log_size; + work_ptr = work_ptr + sizeof(ULONG); + + /* Remember the maximum number of events. */ + _tx_el_maximum_events = event_log_size/TX_EL_EVENT_SIZE; + + /* Setup max_events field. */ + *((ULONG *) work_ptr) = _tx_el_maximum_events; + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup the evploc (location of event pool). */ + *((ULONG *) work_ptr) = (ULONG) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Save the current event pointer. */ + _tx_el_current_event = (UCHAR **) work_ptr; + + /* Setup event_ptr (pointer to oldest event) field to the start + of the event pool. */ + *_tx_el_current_event = (UCHAR *) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup tbfreq (the number of ticks in a second) field. */ + *((ULONG *) work_ptr) = TX_EL_TICKS_PER_SECOND; + work_ptr = work_ptr + sizeof(ULONG); + + /* At this point we are pointing at the Thread Name Information (TNI) array. */ + + /* Remember the start of this for future updates. */ + _tx_el_tni_start = work_ptr; + + /* Clear the entire TNI array, this is the initial setting. */ + end_ptr = work_ptr + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE); + memset((void *)work_ptr, 0, (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = end_ptr; + + /* At this point, we are pointing at the actual Event Entry area. */ + + /* Remember the start of the actual event log area. */ + _tx_el_event_area_start = work_ptr; + + /* Clear the entire Event area. */ + end_ptr = work_ptr + event_log_size; + memset((void *)work_ptr, 0, event_log_size); + work_ptr = end_ptr; + + /* Save the end pointer for later use. */ + _tx_el_event_area_end = work_ptr; + + /* Setup an entry to resolve all activities from initialization and from + an idle system. */ + work_ptr = _tx_el_tni_start; + read_ptr = (UCHAR *) "Initialization/System Idle"; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID to NULL. */ + *((ULONG *) (_tx_el_tni_start + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) TX_NULL; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (_tx_el_tni_start + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Clear the time base global variables. */ + _tx_el_time_base_upper = 0; + _tx_el_time_base_lower = 0; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_register(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT i; + + + /* First of all, search for a free slot in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is available. */ + if (*(entry_ptr + TX_EL_TNI_VALID_OFFSET) == TX_EL_INVALID_ENTRY) + break; + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Check to see if there were no more valid entries. */ + if (i >= TX_EL_TNIS) + return(TX_EL_NO_MORE_TNI_ROOM); + + /* Otherwise, we have room in the TNI and a valid record. */ + + /* Setup the thread's name. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) thread_ptr; + + /* Setup the thread priority. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_PRIORITY_OFF)) = (ULONG) thread_ptr -> tx_thread_priority; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (entry_ptr + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Thread name has been registered. */ + return(TX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT found; +UINT i, j; + + + /* First of all, search for a match in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is a match. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + found = TX_TRUE; + j = 0; + do + { + + /* Determine if this character is the same. */ + if (*work_ptr != *read_ptr) + { + + /* Set found to false and fall out of the loop. */ + found = TX_FALSE; + break; + } + else if (*work_ptr == 0) + { + + /* Null terminated, just break the loop. */ + break; + } + else + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + } + + /* Increment the character count. */ + j++; + + } while(j < TX_EL_TNIS); + + + /* Was a match found? */ + if (found) + { + + /* Yes, mark the entry as available now. */ + *(entry_ptr + TX_EL_TNI_VALID_OFFSET) = TX_EL_INVALID_ENTRY; + + /* Get out of the loop! */ + break; + } + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Determine status to return. */ + if (found) + return(TX_SUCCESS); + else + return(TX_EL_NAME_NOT_FOUND); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_user_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user event into the event log. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* sub_type Event subtype for kernel call */ +/* info_1 First information field */ +/* info_2 Second information field */ +/* info_3 Third information field */ +/* info_4 Fourth information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT upper_tb; +UCHAR *entry_ptr; + + /* Disable interrupts. */ + TX_DISABLE + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_USER_EVENT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) sub_type; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) info_1; + + /* Store the second info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) = + (ULONG) info_2; + + /* Store the third info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) = + (ULONG) info_3; + + /* Store the fourth info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) = + (ULONG) info_4; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + /* Restore interrupts. */ + TX_RESTORE +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_running PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread change event into the event */ +/* log, which indicates that a context switch is taking place. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_schedule ThreadX scheduler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_running(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) 0; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) thread_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_preempted PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread preempted event into the event */ +/* log, which indicates that an interrupt occurred that made a higher */ +/* priority thread ready for execution. In this case, the previously */ +/* executing thread has an event entered to indicate it is no longer */ +/* running. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_context_restore ThreadX context restore */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_STATUS_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_READY; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt event into the log, which */ +/* indicates the start of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_INTERRUPT_SUB_TYPE; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt end event into the log, which */ +/* indicates the end of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt_end(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_END_OF_INTERRUPT; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_control PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function remaps the tx_interrupt_control service call so that */ +/* it can be tracked in the event log. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt posture */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_interrupt_control Interrupt control service */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_interrupt_control(UINT new_posture) +{ + +TX_INTERRUPT_SAVE_AREA +UINT old_posture; + + + TX_EL_NO_INTERRUPT_EVENTS + + TX_DISABLE + TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_INTERRUPT_CONTROL, _tx_thread_current_ptr, new_posture) + TX_RESTORE + + TX_EL_END_FILTER + + old_posture = _tx_thread_interrupt_control(new_posture); + return(old_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_on PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables all event filters. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_on(void) +{ + + /* Disable all event filters. */ + _tx_el_event_filter = TX_EL_ENABLE_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_off PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets all event filters, thereby turning event */ +/* logging off. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_off(void) +{ + + /* Set all event filters. */ + _tx_el_event_filter = TX_EL_FILTER_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the events filters specified by the user. */ +/* */ +/* INPUT */ +/* */ +/* filter Events to filter */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_filter_set(UINT filter) +{ + + /* Apply the user event filter. */ + _tx_el_event_filter = filter; +} + diff --git a/ports/cortex_r4/ghs/src/tx_ghs.c b/ports/cortex_r4/ghs/src/tx_ghs.c new file mode 100644 index 00000000..30b8054e --- /dev/null +++ b/ports/cortex_r4/ghs/src/tx_ghs.c @@ -0,0 +1,485 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" +#include +#include + +/* Allow these routines to access the following ThreadX global variables. */ +extern ULONG _tx_thread_created_count; +extern TX_THREAD *_tx_thread_created_ptr; +extern TX_THREAD *_tx_thread_current_ptr; + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +/* Thread-local storage routines for Green Hills releases 5.x and above. */ +/* + Thread-Local (Per-Thread) Library Data Retrieval + ================================================ + + __ghs_ThreadLocalStorage_specifier defines all library data items + that the Green Hills libraries allow to be allocated per-thread. + + An implementation can choose which of these data items to allocate + for each thread. For example, an implementation may choose to + allocate an errno value for each thread, but not the strtok_saved_pos + pointer. The application could then use strtok_r instead of strtok for + correct operation. + + To add per-thread library data, define one of the + TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item + or items in each thread control block TX_THREAD. + + If C++ with exceptions is being used, the __eh_globals entry must be + allocated for each thread. This is typically done by default using + TX_THREAD_EXTENSION_1 in tx_port.h. + + If __ghs_GetThreadLocalStorageItem is customized to return a + per-thread errno value, you should also: + + * Customize the System Library for your project + * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in + src/libsys/ind_errn.c + + If you customize the System Library, you should remove ind_thrd.c + from the libsys.gpj subproject. + + */ + +/* Provide global __eh_globals value to support C++ exception handling + outside a thread context. This name also forces this module to be + included in the linked program instead of the ind_thrd.o module from + the System Library libsys.a. + */ +static void *__eh_globals; + +#pragma ghs startnomisra +void *__ghs_GetThreadLocalStorageItem(int specifier) +{ + void *ptlsitem = (void *)0; + switch (specifier) { + case (int)__ghs_TLS_Errno: + /* Set ptslsitem to the address of the per-thread errno value. + The per-thread errno value should have the type int. + + If returning a per-thread errno value, follow the steps + above. + + This item is used by numerous library functions. + */ + break; + case (int)__ghs_TLS_SignalHandlers: + /* Set ptslsitem to the address of the per-thread SignalHandlers + array. The per-thread SignalHandlers array should have the + array type as in the following declaration: + SignalHandler SignalHandlers[_SIGMAX]; + The SignalHandler type and _SIGMAX constant are defined in + ind_thrd.h. + + This item is used by the library functions signal() and + raise(). + */ + break; + case (int)__ghs_TLS_asctime_buff: + /* Set ptslsitem to the address of the per-thread asctime_buff + array. The per-thread asctime_buff array should have the + array type as in the following declaration: + char asctime_buff[30]; + + This item is used by the library functions asctime() and + ctime(). The library provides asctime_r() and ctime_r(), + inherently thread-safe versions of these functions. + */ + break; + case (int)__ghs_TLS_tmpnam_space: + /* Set ptslsitem to the address of the per-thread tmpnam_space + array. The per-thread tmpnam_space array should have the + array type as in the following declaration: + char tmpnam_space[L_tmpnam]; + The constant is defined in + + This item is used by the library function tmpnam() when + passed NULL. The library provides tmpnam_r(), an + inherently thread-safe version of tmpnam(). + */ + break; + case (int)__ghs_TLS_strtok_saved_pos: + /* Set ptslsitem to the address of the per-thread + strtok_saved_pos pointer. The per-thread strtok_saved_pos + pointer should have the type "char *". + + This item is used by the library function strtok(). + The library provides strtok_r(), an inherently thread-safe + version of strtok(). + */ + break; + case (int)__ghs_TLS_gmtime_temp: + /* Set ptslsitem to the address of the per-thread gmtime_temp + value. The per-thread gmtime_temp value should have the + type "struct tm" defined in time.h, included by indos.h. + + This item is used by the library functions gmtime() and + localtime(). The library provides gmtime_r() and + localtime_r(), inherently thread-safe versions of these + functions. + */ + break; + case (int)__ghs_TLS___eh_globals: + /* Set ptslsitem to the address of the per-thread __eh_globals + value. The per-thread __eh_globals value should have the + type "void *". + + This item is used by C++ exception handling. + */ + if (_tx_thread_current_ptr) + ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + ptlsitem = (void *)&__eh_globals; + break; + } + return ptlsitem; +} +#pragma ghs endnomisra +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ + +/* + * ThreadX C and C++ thread-safe library support routines. + * + * This implementation merely tries to guarantee thread safety within + * individual C library calls such as malloc() and free(), but it does + * not attempt to solve the problems associated with the following + * multithreaded issues: + * + * 1. Use of errno. This can be made thread-safe by adding errno + * to TX_THREAD_PORT_EXTENSION and using that within a modified + * version of libsys/ind_errno.c. + * + * 2. Thread safety ACROSS library calls. Certain C library calls either + * return pointers to statically-allocated data structures or maintain + * state across calls. These include strtok(), asctime(), gmtime(), + * tmpnam(NULL), signal(). To make such C library routines thread-safe + * would require adding a ThreadLocalStorage struct to the thread control + * block TX_THREAD. Since relatively few applications make use of these + * library routines, the implementation provided here uses a single, global + * ThreadLocalStorage data structure rather than greatly increasing the size + * of the thread control block TX_THREAD. + * + * The ThreadX global variable _tx_thread_current_ptr points to the + * current thread's control block TX_THREAD. If a ThreadLocalStorage struct + * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage + * should be modified to return &(_tx_thread_current_ptr->tx_tls). + */ + +static ThreadLocalStorage GlobalTLS; + +ThreadLocalStorage *GetThreadLocalStorage() +{ + return &GlobalTLS; +} +#endif + +/* + * Use a global ThreadX mutex to implement thread safety within C and C++ + * library routines. + * + */ +TX_MUTEX __ghLockMutex; + +/* + * Acquire general lock. Blocks until the lock becomes available. + * Use tx_mutex_get to implement __ghsLock + */ +void __ghsLock(void) +{ + tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER); +} + +/* + * Release general lock + * Use tx_mutex_put to implement __ghsUnlock + */ +void __ghsUnlock(void) +{ + tx_mutex_put(&__ghLockMutex); +} + +/* ThreadX Initialization function prototype. */ +void _tx_initialize_kernel_setup(void); + +void __gh_lock_init(void) +{ + /* Initialize the low-level portions of ThreadX. */ + _tx_initialize_kernel_setup(); + + /* Create the global thread lock mutex. */ + tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT); +} + +/* + Saving State Across setjmp() Calls + ================================== + + These routines can be used to save and restore arbitrary state + across calls to setjmp() and longjmp(). +*/ +int __ghs_SaveSignalContext(jmp_buf jmpbuf) +{ + return 0; +} + +/* Restore arbitrary state across a longjmp() */ +void __ghs_RestoreSignalContext(jmp_buf jmpbuf) +{ +} + +#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560) +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +#else + if (_tx_thread_current_ptr) + + /* Return thread-specific __eh_globals pointer. */ + return _tx_thread_current_ptr->tx_thread_eh_globals; + else + /* Return the global __eh_globals pointer. */ + return GlobalTLS.__eh_globals; +#endif +} +#endif + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +#pragma weak __cpp_exception_init +extern void __cpp_exception_init(void **); +#pragma weak __cpp_exception_cleanup +extern void __cpp_exception_cleanup(void **); + +/* __tx_cpp_exception_init retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_init. + */ +void __tx_cpp_exception_init(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_init) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_init(peh_globals); + } +} + +/* __tx_cpp_exception_cleanup retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_cleanup. + */ +void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_cleanup) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_cleanup(peh_globals); + } +} + +/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize + exceptions for the global context. + */ +void __ghs_cpp_exception_init() { + __tx_cpp_exception_init((void *)0); +} + +/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up + exceptions for the global context. + */ +void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + __tx_cpp_exception_cleanup((void *)0); +} +#endif + + +/* + File Locks + ====================== + + These routines can be customized to implement per-file locks to allow + thread-safe I/O. + +*/ + +/* Acquire lock for FILE *addr */ +void __ghs_flock_file(void *addr) +{ + tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER); +} + +/* Release lock for FILE *addr */ +void __ghs_funlock_file(void *addr) +{ + tx_mutex_put((TX_MUTEX *)addr); +} + +/* Non blocking acquire lock for FILE *addr. May return -1 if */ +/* not implemented. Returns 0 on success and nonzero otherwise. */ +int __ghs_ftrylock_file(void *addr) +{ + return -1; +} + +/* Calls to initialize local lock data structures before they */ +/* are used. */ +void __ghs_flock_create(void **addr) +{ + *addr = (void *)(&__ghLockMutex); +} +void __ghs_flock_destroy(void *addr) {} + + +/* + * ThreadX Peak Stack Checking support routines. + * + * All of these routines are called by MULTI's ThreadX-aware debugging + * package to determine the peak stack use for one thread or for all threads. + * + * These routines are included in this file in order to guarantee that they will + * be available while debugging with MULTI. These routines are not referenced by + * any other part of the ThreadX system. + * + * _txs_thread_stack_check: return the peak stack usage for a thread. + * + * _txs_thread_stack_check_2: store the peak stack usage for all threads + * in the tx_thread_stack_size field of each thread + * control block, TX_THREAD. This routine takes + * advantage of the redundancy within the TX_THREAD + * structure since tx_thread_stack_size can be computed + * from the tx_thread_stack_start and tx_thread_stack_end + * fields of TX_THREAD. + * + * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2 + * call by computing the stack size for each + * thread and storing the result in the + * tx_thread_stack_size field of each thread control + * block TX_THREAD. + * + * These three routines do not support architectures such as i960 or StarCore + * where the stack grows up instead of down. + * + */ +#ifndef TX_DISABLE_STACK_CHECKING + +ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr) +{ + CHAR *cp; /* Pointer inside thread's stack. */ + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start; + cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Return the number of bytes from cp up to and including the + end of the stack. */ + return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1); + } + } + return thread_ptr->tx_thread_stack_size; +} + + +int _txs_thread_stack_check_2(void) { + CHAR * cp; /* Pointer inside thread's stack. */ + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end; + ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Store the number of bytes from cp up to and including the + end of the stack in the tx_thread_stack_size field. */ + tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1; + break; + } + + } + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +int _txs_thread_stack_check_2_fixup(void) { + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and + tx_thread_stack_start fields. */ + tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1; + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +#endif /* TX_DISABLE_STACK_CHECKING */ diff --git a/ports/cortex_r4/ghs/src/tx_ghse.c b/ports/cortex_r4/ghs/src/tx_ghse.c new file mode 100644 index 00000000..6369df77 --- /dev/null +++ b/ports/cortex_r4/ghs/src/tx_ghse.c @@ -0,0 +1,49 @@ +/* + * ThreadX C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 560) +#ifdef _WIN32 +/* Windows uses a different linker, so include a stub routine, never called, + to pull in __cpp_exception_init and __cpp_exception_cleanup */ +extern void __cpp_exception_init(void **); +extern void __cpp_exception_cleanup(void **); +void __tx_win32_pull_in_exceptions(void) { + __cpp_exception_init(0); + __cpp_exception_cleanup(0); +} +#else +#pragma ghs reference __cpp_exception_init +#pragma ghs reference __cpp_exception_cleanup +#endif + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +} +#endif diff --git a/ports/cortex_r4/green/src/tx_thread_context_restore.arm b/ports/cortex_r4/ghs/src/tx_thread_context_restore.arm similarity index 96% rename from ports/cortex_r4/green/src/tx_thread_context_restore.arm rename to ports/cortex_r4/ghs/src/tx_thread_context_restore.arm index c4f3c21c..9ebb0e41 100644 --- a/ports/cortex_r4/green/src/tx_thread_context_restore.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -114,13 +114,13 @@ _tx_thread_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -132,7 +132,7 @@ _tx_thread_context_restore: __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -209,7 +209,7 @@ __tx_thread_preempt_restore: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - + STR r2, [r0, 24] # Save thread's time-slice MOV r2, 0 # Clear value STR r2, [r3] # Disable global time-slice flag diff --git a/ports/cortex_r4/green/src/tx_thread_context_save.arm b/ports/cortex_r4/ghs/src/tx_thread_context_save.arm similarity index 94% rename from ports/cortex_r4/green/src/tx_thread_context_save.arm rename to ports/cortex_r4/ghs/src/tx_thread_context_save.arm index 3b9c1484..54823271 100644 --- a/ports/cortex_r4/green/src/tx_thread_context_save.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,40 +39,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -90,7 +90,7 @@ _tx_thread_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers #ifdef TX_ENABLE_FIQ_SUPPORT #ifdef TX_BEFORE_ARMV6 @@ -116,7 +116,7 @@ _tx_thread_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -132,7 +132,7 @@ _tx_thread_context_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing __tx_thread_not_nested_save: /* } */ @@ -146,13 +146,13 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, r10, r12, lr} # Store other registers /* Save the current stack pointer in the thread's control block. */ @@ -172,7 +172,7 @@ __tx_thread_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing /* } else @@ -182,7 +182,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit @@ -197,7 +197,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, 16 # Recover saved registers - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing .type _tx_thread_context_save,$function .size _tx_thread_context_save,.-_tx_thread_context_save diff --git a/ports/cortex_r4/green/src/tx_thread_fiq_context_restore.arm b/ports/cortex_r4/ghs/src/tx_thread_fiq_context_restore.arm similarity index 96% rename from ports/cortex_r4/green/src/tx_thread_fiq_context_restore.arm rename to ports/cortex_r4/ghs/src/tx_thread_fiq_context_restore.arm index f2784a06..60967391 100644 --- a/ports/cortex_r4/green/src/tx_thread_fiq_context_restore.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_fiq_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -42,41 +42,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_restore Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_restore Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the fiq interrupt context when processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* FIQ ISR Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the fiq interrupt context when processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* FIQ ISR Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -111,13 +111,13 @@ _tx_thread_fiq_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_fiq_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -129,7 +129,7 @@ _tx_thread_fiq_context_restore: __tx_thread_fiq_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ diff --git a/ports/cortex_r4/green/src/tx_thread_fiq_context_save.arm b/ports/cortex_r4/ghs/src/tx_thread_fiq_context_save.arm similarity index 93% rename from ports/cortex_r4/green/src/tx_thread_fiq_context_save.arm rename to ports/cortex_r4/ghs/src/tx_thread_fiq_context_save.arm index 604c9110..3251ecd0 100644 --- a/ports/cortex_r4/green/src/tx_thread_fiq_context_save.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_fiq_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -33,40 +33,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_save Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_save Cortex-R4/GHS */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -84,7 +84,7 @@ _tx_thread_fiq_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state CMP r2, 0 # Is this the first interrupt? @@ -99,7 +99,7 @@ _tx_thread_fiq_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -115,7 +115,7 @@ _tx_thread_fiq_context_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing __tx_thread_fiq_not_nested_save: /* } */ @@ -129,16 +129,16 @@ __tx_thread_fiq_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, lr} # Store other registers, Note that we don't - /* # need to save sl and ip since FIQ has - # copies of these registers. Nested + /* # need to save sl and ip since FIQ has + # copies of these registers. Nested # interrupt processing does need to save # these registers. */ @@ -159,7 +159,7 @@ __tx_thread_fiq_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing /* } else @@ -179,15 +179,15 @@ __tx_thread_fiq_idle_system_save: #endif /* Not much to do here, save the current SPSR and LR for possible - use in IRQ interrupted in idle system conditions, and return to + use in IRQ interrupted in idle system conditions, and return to FIQ interrupt processing. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, lr} # Store other registers that will get used - /* # or stripped off the stack in context + /* # or stripped off the stack in context # restore */ - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing .type _tx_thread_fiq_context_save,$function .size _tx_thread_fiq_context_save,.-_tx_thread_fiq_context_save diff --git a/ports/cortex_r4/green/src/tx_thread_fiq_nesting_end.arm b/ports/cortex_r4/ghs/src/tx_thread_fiq_nesting_end.arm similarity index 91% rename from ports/cortex_r4/green/src/tx_thread_fiq_nesting_end.arm rename to ports/cortex_r4/ghs/src/tx_thread_fiq_nesting_end.arm index c833a268..ece49230 100644 --- a/ports/cortex_r4/green/src/tx_thread_fiq_nesting_end.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_fiq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,48 +41,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_end Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_end Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ -/* processing from system mode back to FIQ mode prior to the ISR */ -/* calling _tx_thread_fiq_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ +/* processing from system mode back to FIQ mode prior to the ISR */ +/* calling _tx_thread_fiq_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_fiq_nesting_start.arm b/ports/cortex_r4/ghs/src/tx_thread_fiq_nesting_start.arm similarity index 92% rename from ports/cortex_r4/green/src/tx_thread_fiq_nesting_start.arm rename to ports/cortex_r4/ghs/src/tx_thread_fiq_nesting_start.arm index e1cc76fd..9f0e414e 100644 --- a/ports/cortex_r4/green/src/tx_thread_fiq_nesting_start.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_fiq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,45 +39,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_start Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_start Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_context_save has been called and switches the FIQ */ -/* processing to the system mode so nested FIQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_context_save has been called and switches the FIQ */ +/* processing to the system mode so nested FIQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_interrupt_control.arm b/ports/cortex_r4/ghs/src/tx_thread_interrupt_control.arm similarity index 92% rename from ports/cortex_r4/green/src/tx_thread_interrupt_control.arm rename to ports/cortex_r4/ghs/src/tx_thread_interrupt_control.arm index faf3a08e..0119775b 100644 --- a/ports/cortex_r4/green/src/tx_thread_interrupt_control.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_interrupt_control.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,39 +38,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_interrupt_disable.arm b/ports/cortex_r4/ghs/src/tx_thread_interrupt_disable.arm similarity index 92% rename from ports/cortex_r4/green/src/tx_thread_interrupt_disable.arm rename to ports/cortex_r4/ghs/src/tx_thread_interrupt_disable.arm index 4993f0b2..f97f3c03 100644 --- a/ports/cortex_r4/green/src/tx_thread_interrupt_disable.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_interrupt_disable.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,38 +38,38 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_interrupt_restore.arm b/ports/cortex_r4/ghs/src/tx_thread_interrupt_restore.arm similarity index 92% rename from ports/cortex_r4/green/src/tx_thread_interrupt_restore.arm rename to ports/cortex_r4/ghs/src/tx_thread_interrupt_restore.arm index 6b09c8bf..455a6d57 100644 --- a/ports/cortex_r4/green/src/tx_thread_interrupt_restore.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_interrupt_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -31,39 +31,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_irq_nesting_end.arm b/ports/cortex_r4/ghs/src/tx_thread_irq_nesting_end.arm similarity index 91% rename from ports/cortex_r4/green/src/tx_thread_irq_nesting_end.arm rename to ports/cortex_r4/ghs/src/tx_thread_irq_nesting_end.arm index 7681137b..56524c1d 100644 --- a/ports/cortex_r4/green/src/tx_thread_irq_nesting_end.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_irq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -40,48 +40,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_end Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_end Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ -/* processing from system mode back to IRQ mode prior to the ISR */ -/* calling _tx_thread_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ +/* processing from system mode back to IRQ mode prior to the ISR */ +/* calling _tx_thread_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_irq_nesting_start.arm b/ports/cortex_r4/ghs/src/tx_thread_irq_nesting_start.arm similarity index 92% rename from ports/cortex_r4/green/src/tx_thread_irq_nesting_start.arm rename to ports/cortex_r4/ghs/src/tx_thread_irq_nesting_start.arm index 1d52eab0..aa60b4b8 100644 --- a/ports/cortex_r4/green/src/tx_thread_irq_nesting_start.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_irq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,45 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_start Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_start Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_context_save has been called and switches the IRQ */ -/* processing to the system mode so nested IRQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_context_save has been called and switches the IRQ */ +/* processing to the system mode so nested IRQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r4/green/src/tx_thread_schedule.arm b/ports/cortex_r4/ghs/src/tx_thread_schedule.arm similarity index 94% rename from ports/cortex_r4/green/src/tx_thread_schedule.arm rename to ports/cortex_r4/ghs/src/tx_thread_schedule.arm index 1fd4cf50..f19a1e8c 100644 --- a/ports/cortex_r4/green/src/tx_thread_schedule.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_schedule.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,42 +38,42 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -111,7 +111,7 @@ __tx_thread_schedule_loop: /* } while(_tx_thread_execute_ptr == TX_NULL); */ - + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ @@ -134,7 +134,7 @@ __tx_thread_schedule_loop: MOV r0, v1 # Restore temp register #endif - LDR r1, =_tx_thread_current_ptr # Pickup address of current thread + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread STR r0, [r1] # Setup current thread pointer /* Increment the run count for this thread. */ @@ -148,7 +148,7 @@ __tx_thread_schedule_loop: /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - LDR r2, =_tx_timer_time_slice # Pickup address of time slice + LDR r2, =_tx_timer_time_slice # Pickup address of time slice /* # variable */ LDR sp, [r0, 8] # Switch stack pointers STR r3, [r2] # Setup time-slice diff --git a/ports/cortex_r4/green/src/tx_thread_stack_build.arm b/ports/cortex_r4/ghs/src/tx_thread_stack_build.arm similarity index 96% rename from ports/cortex_r4/green/src/tx_thread_stack_build.arm rename to ports/cortex_r4/ghs/src/tx_thread_stack_build.arm index d6e9ce66..3dbdd96a 100644 --- a/ports/cortex_r4/green/src/tx_thread_stack_build.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_stack_build.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ /* thread_ptr Pointer to thread control blk */ /* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -86,10 +86,10 @@ .globl _tx_thread_stack_build _tx_thread_stack_build: - + /* Build a fake interrupt frame. The form of the fake interrupt stack on the Cortex-R4 should look like the following after it is built: - + Stack Top: 1 Interrupt stack frame type CPSR Initial value for CPSR r0 (a1) Initial value for r0 diff --git a/ports/cortex_r4/green/src/tx_thread_system_return.arm b/ports/cortex_r4/ghs/src/tx_thread_system_return.arm similarity index 94% rename from ports/cortex_r4/green/src/tx_thread_system_return.arm rename to ports/cortex_r4/ghs/src/tx_thread_system_return.arm index 93c34aac..090cb35d 100644 --- a/ports/cortex_r4/green/src/tx_thread_system_return.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_system_return.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -37,41 +37,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -87,7 +87,7 @@ _tx_thread_system_return: MOV r0, 0 # Build a solicited stack type MRS r1, CPSR # Pickup the CPSR STMDB sp!, {r0-r1, r4-r11, lr} # Save minimal context - + /* Lockout interrupts. */ #ifdef TX_BEFORE_ARMV6 diff --git a/ports/cortex_r4/green/src/tx_thread_vectored_context_save.arm b/ports/cortex_r4/ghs/src/tx_thread_vectored_context_save.arm similarity index 95% rename from ports/cortex_r4/green/src/tx_thread_vectored_context_save.arm rename to ports/cortex_r4/ghs/src/tx_thread_vectored_context_save.arm index a41cff9f..1b415477 100644 --- a/ports/cortex_r4/green/src/tx_thread_vectored_context_save.arm +++ b/ports/cortex_r4/ghs/src/tx_thread_vectored_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,40 +39,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_vectored_context_save Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_vectored_context_save Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -139,7 +139,7 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Note: Minimal context of interrupted thread is already saved. */ @@ -171,7 +171,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit diff --git a/ports/cortex_r4/green/src/tx_timer_interrupt.arm b/ports/cortex_r4/ghs/src/tx_timer_interrupt.arm similarity index 95% rename from ports/cortex_r4/green/src/tx_timer_interrupt.arm rename to ports/cortex_r4/ghs/src/tx_timer_interrupt.arm index 36ce56a3..004c8a29 100644 --- a/ports/cortex_r4/green/src/tx_timer_interrupt.arm +++ b/ports/cortex_r4/ghs/src/tx_timer_interrupt.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ @@ -32,43 +32,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-R4/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-R4/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Process timer expiration */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Process timer expiration */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -95,7 +95,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR r3, =_tx_timer_time_slice # Pickup address of time-slice + LDR r3, =_tx_timer_time_slice # Pickup address of time-slice LDR r2, [r3] # Pickup time-slice CMP r2, 0 # Is it non-active? BEQ __tx_timer_no_time_slice # Yes, skip time-slice processing @@ -212,7 +212,7 @@ __tx_timer_dont_activate: /* if (_tx_timer_expired_time_slice) { */ - LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired + LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired LDR r2, [r3] # Pickup the actual flag CMP r2, 0 # See if the flag is set BEQ __tx_timer_not_ts_expiration # No, skip time-slice processing diff --git a/ports/cortex_r4/ghs/src/txr_ghs.c b/ports/cortex_r4/ghs/src/txr_ghs.c new file mode 100644 index 00000000..19572e2b --- /dev/null +++ b/ports/cortex_r4/ghs/src/txr_ghs.c @@ -0,0 +1,84 @@ +/* + * ThreadX API Runtime Error Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +/* #include "tx_ghs.h" */ +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* Customized ThreadX API runtime error support routine. */ + +void _rnerr(int num, int linenum, const char*str, void*ptr, ...); + +/* __ghs_rnerr() + This is the custom runtime error checking routine. + This implementation uses the existing __rnerr() routine. + Another implementation could use the .syscall mechanism, + provided MULTI was modified to understand that. + */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal) { + TX_INTERRUPT_SAVE_AREA + int num; + /* + Initialize the stack levels value. + + Add 3 to account for the calls to _rnerr, __rnerr, and + __ghs_rnerr. + + If the implementation changes, calls to __ghs_rnerr + will not need to be changed. + + Zero is not permitted, so substitute 3 in that case. + */ + num = (stackLevels+3) & 0xf; + if (!num) { + num = 3; + } + /* + Shift the stack levels value to bits 12..15 and + insert the stack trace display value in bit 11. + Bits 0..10 are unused. + */ + num = (num << 12) | (stackTraceDisplay ? 0x800 : 0); + + /* This will mask all interrupts in the RTEC code, which is probably + unacceptable for many targets. */ + TX_DISABLE + _rnerr(num, -1, (const char *)hexVal, (void *)errMsg); + TX_RESTORE +} + + +/* ThreadX thread stack checking runtime support routine. */ + +extern char __ghsbegin_stack[]; +extern TX_THREAD *_tx_thread_current_ptr; + +void __stkchk(void) { + int i; + if(_tx_thread_current_ptr) + { + if((unsigned)(&i) <= + (unsigned)(_tx_thread_current_ptr -> tx_thread_stack_start)) + { + _rnerr(21, -1, 0, 0); + } + } + else + { + if((unsigned)(&i) <= (unsigned)__ghsbegin_stack) + { + _rnerr(21, -1, 0, 0); + } + } +} diff --git a/ports/cortex_r4/gnu/example_build/libc.a b/ports/cortex_r4/gnu/example_build/libc.a deleted file mode 100644 index 5b04fa4ed9b6479f70979f5577dd0705d1f6d1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2447586 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

Q>dP6leU^u3WJIStz4({jFOv z9|o_G`9Q}YAJ?@w)<`ES%4ePa6BXsNO8=RP&Qp|SGyFdk<@X5vSCV4fUZp7a2lZ$@ z4Da%VOP0@Hy5v%^WwdLVGM!Ed*Y#e}GC5)FAyaBu603yWlOth%Mc9AAs+ckbBle35 zIZ~T2IV}GmzrSCGM?Fi1^wzUW@WUV%V=g7G<*o3m;0XvKL;U009S5-=Mqf_AFM?Q( ziVMCAA4SFBA0n;b7vu!*ir}CLB*+aY<3UuEVq$+&7(_%P36^`_rA+LJg7>}LXYOUD zO)FxA6k&qRdN@@4oH9>9|^EM`Oh6_gap~mjC8D!VDyrr1Rj?dk~({yiiW}^9As`{BDIOfiTKzn12nwZ27MV{Mh%}>9Vrr zzq_3J;T322Wy^p3s!eHTpqgI;uHE|G>(q}@gl=!P{5J~x@-!30kKV278br8Kh(c4e z2UKUtmj8G%&#-pVWy^n8AzgRmzwNo*lmB+$rcP|9Tqj6e>Uz_&Kjc4-5$oL2KN|V3 zKoyn(rTjO-7B0rCkpG;?AKNptUU2ZvU%J%De;Ckg!`{e$ESvILTG0YDAI^%m-63H-9N|d1vz2tDgSx%o3y?V?E&hu$-2Au(lrCv z=}MxbA|xI#^beer0x8IA=Rc1tySwwked*M>}ptJo1 zNZ{Sc|9vAf@}BP{e&`yG@yz(?P(x@8MbFZX_7(D-p&f6DmOc3{m$&ONT-D>JOTuOQ zQm1=Id%pH(#nFt_J{wFawOs|F>J_VDAtnv=l-crV zN9p*E(Rs*)>CWecayo7r{Q8VB!hwXOr%Ou~dPnitCHe!c*Hw|>*XFM+VG4xtQd6f6kI=A$XMjq{F3lrm$^8+2{4$7xy z{>N-BWKjY8R4kcLZDPdQ%@8p9sMa56p)QnD=O}ufqVpA9tSFxmrqkh`K`gz`O%s z-(D^7Zx7gad1EuN?;M6z$}sxz)!!NykIn2>KULG@r>}1DDmUB_B{tl_@X*@=3zpaH z$_0;n$h_}p8`$3L)|}LpNPkib(!Pv5FTcBc_p4rdLtB*E(Dn}OVz(<7x3eZn?IV3a zZBC{&>t305t8-luIwtHu3&YUvNw(BVa;!z?WhyDfGnJIQ-=Obs+u@{myb0_FnHLO}zR1fJV{XWDCiSbATm(ekXFfVs4|+n&S3@1>TxNi3&>u;p=gjy@ zL^CnfguI{g!utmQ_l@+ekovt7?2K*?+Ju<`^M+(6k9EVMxVR7DZ#{p>5#x^-59vD` zJah55nxjtrTzXs$MDYmAz<6{s@PAi+VdqTmTX?;HO>lL1RkSW=Mqio?Qup_FxcVA? z>`^-ARCiwZ`9}`geP;Cv2+oUFtX!3%2)%c0RZmbr-~ z^9)ZrvV3*T@)gUZ?Y%u^{{z_n-YMW<2(>3`yQ53XfZ4J(b-lF=$TvIF#qi6PwRa%Q zbxcc_Eo<)(q4rOw%a*m7?sl8Z$U4&yrt>hm8Xi7L+UdA#S-TSL*oH)1$=b)`5#$u9 zo#vM#>vTfSwe$JM)6X2)&8C5m>>TvDyn+|@1G&3iT zasdC4ocDer(gPAKQl$l2; znpU)JBQ_?r~{4k_jX%1We9s_^Z~&u70ox-!+ZEAi@EXyop$WNsWG zqZWs|-WwncHRJJ~gu3vn{$R{V0c2s?lJa&y&TeXK#y!i!FAW%?-7Q)M=b^k^j7#P5h)@AEE@@ z8gU!iT-Sm+O!U%g+eVjfLBGD-wma>$u&!epH?-jWls5|3c$V{adtQrQepSnN$lpo# zH`FD@I-LCIt^_E@hVT8gZFj{4Q-*eBQ zcSiT%7a@;M|BlM%^5FFv_I>J>wmh$~AmKHJ)rr}-=ZytX<8>`JqYclqe=2Wj>xF(~ z*gAwg%`oIa`c|ZE#dqMFUj3_WBa3cpi}^Rc&UBX|-D3`Tk$S>lP^;q8S@(I-`(GXPTKd>wabIou%prHry5(? zmOiK->+Yo*o7s*}!yi>%-NNtVl-Ky=CwA|Cmi2vQ_wMu7qaBfV^Uf2I2KVtzKmDtn zq5qZFkB9#+@K>kKX#w8?@+a@VeX#paYrJy=Z`-d(gMQt~K2a_`e!|2_M;d(w1cqBLrZK}V zM>8u@>kBvoiz<*KOp3TpQEb&DnPOz7(tu7iY9Ma=p>b6AOuUE>i~A&_RaP*Ur&x`# zywtH)e*{ZBsBn=GM`mCo(~Uu(KNCY;1A>gsf%YX5{u!4z*6MZ;9BcJMBk(D(0$0$o;R8d#qJhz-5@|ebh@GYc&$72Zm$fUq(=PI8D4@oun@g9?*7F zqBxHp(Y{#x5yzJw-@ZVmx$lZxXIF27{A4b z&m3ztpDE3;Ry!GYj7*vG61C=^!thPDUDxbVEjai2{$$69^B5;Bgp3V$rQ zOpjm3@_s7w&5GADd#qI}6We30e#yj{$6AHwR&CNgu5wfYg`vyQb2_dM2$yF9|se+8)rti~JWVX(;$bN!^4TJ5n`G+Yqc zW386Z+sUz3=c2ZK!x4UR4y$dCwfa5t*<-D~#Y%T_tkrv{mOj=hTpT1n$u6?TTK%5c z?XgxrCiBc=t-^ByPUS)I$Rt%(!}Ee7o=i z*YLg^@n>{+eiYt_>IO@8!ZY}EEGNBz#`_OUL<+iB@WUxrb)J9kWYo@|H}qgs%)gKE z{_LUGAg_Oa5j__STjTi;itzH`wVwY2&edLuC)IwPSpuKDc*>Q3ei85q-~S{S1~=q- zshg3e59V;ckI(oSG7ZYi_eG&+dLw#4EIWzeEsXu8NHO#uB!4Jk>1WBRGzrgapgVrE zsLI^p_nD}b`K8T^VW`_q$#xh|%kg&(KpX%a*1wICl0VmrF^u&S@85nfCGGl$dn3OA zzrTfvkMmg@iizl_h0Ad0*U^&usN(0@m1VT&?VopOEppHeTeS=|m_9%>zL$3x<BN#rC6h`ukhOP^kh=}-Lvg@jyjtkeU>cNoC733T z=8fYWVWy3r`<2u{6B&YvGHqD)8VH3&W>DPbirceq!M@;bT{0g~5b@ucePH{YtoUVc2)(iNO#Ip3e0` z_(;#_8(oiN0y|*i7&a9@Vb?oxz0Y+;IzHN5*AvFRuL{I^DZ}|duNUEk=k%Ro;YF^4 z((&IYPH$mHiIoI|2eD1ZeYkFUucU62{euKX@xH7DdX7rtcv=()I6JwqaZAFE; zje?hr7k5L1=`TEbUPDT3}n=Ie~p{_|ZKVrsAuhXqZgpPC5@m=k_ zEN!i-zxM^AGzK%~FzNxj->5fGJJ_?Mxy%=^8z?1Dj7SoP1aINz&@%n@C)^DfW z?EMa_M$2}QAKS+^kyhd7^1HXd^SENAoo@RD2yp9n8TgeWjQrRf&94sEF270$Ln(x5 zr{l8sJ3QyqFCU3DKYkkPJmgW|0lx;sx%K-c`0@U-e(mjdK(KI);KzHZoi6Kk1n^qN zy?}Hh@WrMb0TieK{t9y3pm&yFd0t#8tVJr33UR+hLtN zTke-8z&EnA42pzZ0WF8?{PasF^K*~3_D{lvmd|y1`gJ^Rqc`30AFpEAr#^c06i_Y4 z>-_XfC(C=m@v}XY>)m+D_}b5S;-%Wp@+-}all?&q(v2_E4`;>*$+n@!lov_0`Dv*fPGSt<-ah?zcS0;kmVlNLo=uL_ePGP!R73KUu|LuxyQ}ixHw<~(Dq7N$ih@v|beM-@%73DZ3U)rlCU99L* zMQasZrRW+(FH^K$(RGSmtLS<~X}6kuZ&Gx#qPHsAsOasAZd3FwMYk(@ucCZzc%P<| zj*;)zF83{^)V?K9Wbpz}&LO0qP;{E2eD^Z^d_@;4x=ztAD0+*c+Z263(I*uBxuUNs z+NS8+ipDrzQke4<<+3pS=P6pHXsx1`E4p4$Vu(z?P0{-meM-?66>U@WzZB&<0rQFT zB|*jclAy=p9x?o6MQIP6{soGDM$vC5`W;1opy(W4ZduIL$xRw}w& zQ8BJiuDIWzPpI(UEBcnA|5P-_ae#COD|)P=l)o7M8%1|1`gcWpK^|ayQqhr$PE>T7 zqO%pPRCI-+mn*tK(OVV$j-n4K`cp+;QFNE0|4=jr0g?3>sA#F8$0<5P(X$m@sOZ&- zeo@h{DSEe}&nx;nMQKZu<@`(0yj-Z)s0=KH}a$>--FU&A=F#fZRuV7D0kgpqcOkr3^=*kaTN z95(vFnX+fi7Ne8M_-t9BC@w&Iv&D#Y33{}}Xg=;~EU1ZdPeow17~RJ*vbPusG2L!4 zdKhWu1!9X);BGMr+$~1IowA~__%^aMTZ|rNcVuia3d|Ox<2lUC7NhsrB(ue6Dt%^) z(QQm=wiqQ@g4tr!hYiZuViaU-F$yxa7zIC+^_&|29=Vt;M*qzcGPW26W{c4jru?bQ zH!Ch)8fJ^p<7}VVVkCffyTxcRndbBt*e|meJjXwJ3+Tm`E+WKHAzJY9+G2Dgs^vw& z^T}zv4R(vsXW0{a6H$~bV667Wl1K7RXm5VeTa4af6YLhF4NPpe81cyr zoGnJ*XSB1$Xd{_9Ta3hqth2>P2qb%MF%sX+&K9HZf=zyaEkBk_&yY%w~R@y-^bdzs(aV)PXj=WH?B$=Y$zYL$(R?uc^M`Rua-g@pc;ndx@?-zwqkUakcS+qo+yZ01 zCBzmKz3l{@W44{(T5UVQmDzTJzBgM<`up5evUDbvc=puq<~Nac>}hRhi8Cr4!}wh@ z20s)4cM@*l|KQeDsLvc6L&dwQd?E=m$;9Hmi!k0o?F1mN%p?7;ZJ=m>$Hd2IpF{-~ zc448thIoE=_dzUNx?(F z3s$dOS+jgqg*Z(QiN(}PZlcbb-NL2w7lFxYwXNz>B>ca-t6`s2l6A;{Hk!R^Wf5}#0z~2FO*|Cx(o1g`Mm{xDTI+9mQM}8?0pQI!EYSmwbNx~ z?_ws*^nPM53wn!4fpbAN&?-xv2{w62WkY2!hAIFhW}7x+8- zosOa=@o`e^*pG+d$1q+qz7$kDhG}`VMA&xVSzcD0g}@~i@FCf15yE{7Nn2~Ez(}we z3TA9~fPF%Ip1Z_N`zYKLbPUsjPEwTjnSRbKqyjeu6}TzrVimqlQGuHxTzsK}Zd2h8 zDEfq=0yjmxz)eBhRQTJ93fvUo0yhN}xGAW>O+f{23Mz0@P=T9*3fvS_;HIDgHw6{A zDX73rL0`n+WP1c|3Mz0@P=T9*3fvS_;HIDgHw6{ADX73rK?QCKDsWR!ft!K~+!R#c zrl0~h1r@j{sK8A@1#Su|a8po$n}UkZV9-iDc&w+uO+f{23Mz0@P=T9*3fvS_;HIDg zHw6{ADX73rK?QCKDsWR!ft!K~+!R#crl0~h1r@j{sK8A@1#Su|a8po$n}Q156jb1* zpaM4q6}Ty=z)e8~ZVD=JQ&54Mf(qOeRN$td0yhN}xGAW>O+f{23Mz0@P=T9*3fvS_ z;HIDgHw6{ADX73rK?QCKDsWR!ft!K~+!R#crl0~h1r@j{sK8A@1#Su|a8po$n}Q15 z6ts@`C#b+pK{qMCz)j)5NBISA3ja&WFK|=%1#Su|a8po$n}Q156jb1*paM4qeJdu* zdsorjisoU`Chpf)(GjFbKTi3_D>_Am7xQ^QdcMP`z@C9e!k=b1;>CP|KTSDrF}!-! ziundy1a8=+{gtDpOppL$*ZAVKEF4|KIf*kH?XS?ibNaY966uX?iP{^K&5}9Ik+-4E z-`LoUop$$3!!Yw}Pj1N_-`HGOc4bSH+R_#!HoachxV9zF+w{7hK-l(%ro76ACfX4Z zHbd%>w`@aO-j2rRBk_3lhK-TP+hS~rjODAe95y4$w!ntPwJo%*$h1-Frkw%oxD?}u z(cb1K8k&l5Jz`fwlRpq)qZ^xxKu3V~MR<^!-^8>ziM36sDGQp8AKlOt_vSW#KlNPm zv~wDp?@c_{RD$%ocJIC=w+LmVZ+xA$M)E4xBAquVsyqp8II9`=;TA7_Gp0}2330;N z?!N2{sdtaC%#ZKyx*@haG2DNAf@^Kf6&-iDTZuYQ-X7Xv#Ux8pt(!uI6%(e_C@XqRPP zYv4b3&m?cdeSW&}9xuJ_zDeL06g}BmnAmd1h{GG3ub9->+V7}_mH^jYV%;5~cSiHw zc;wUR4K1O!Y1fe@Uql%CD0uxs&-4J}J?-5c>iVuRp0DYd0-#4(M{4c9d`Z^O@nosfYTw|AfoiyODJ zEk%99^mR=qT#q_pjHM8tF5B{>RC>k}lO{Gkp7yr9jJE!$EVcNFRC@7?L#8!6o;acL z7qo#R>pZu$IrJV%!*);T1#gT6KiCIt9e&gaEhk(*qh&1Ybq>K859?>Nq*1@n8}i0i zN^P4Db=v=BC)_VbUCSDqIhI(?jc!~TaVd^xJ}=5HPc6bK<@FURZ8_SRi!`*6qWc$9 zR$jR$pRru<=>ZRxTNEmw|jXyU!g z_ii%xt$fE8h_bNvvSU{N@*NwJGf;M3dU2CqHm9W=ec~k==fM8YG}QZqva$_lRwwW) z+i+r8YQu?qp0HQ%p|Uch+tJt>maS_-nzqUv8`?G_tT*p}Sz|*E=w^gRWz(Mk{n6$W z$6!+{`byZvnTo#l%TIh_9?G7c*w8i?GzT`B0@%5ki*4pFU~c7a8-DNPc;0qUzWb)9 zztR?^uWXy1`idBD1rr*ZIfh~gW8V~_{$=UUx556*n;08yhfQs4<@oHEZfMbC6Rh&K zZ@;Z=8~TmU%bBn*6sCXMwoTM=PHQavh5i`70c{$63VbGcTiSL{+JR@|NsR9~t!uX5 zmh)%id7EY6*%*la<#-wB<-ai&W$vE-RBOKryOkv(y&Ea-_SA_eLSxo{fy_tug5c=Xly+g*HIbn0mc)$^OkjGcn74{ z!A{ehX3qNz{~ydx)6s|QSN7>Vw5hLnt{WTtbk2?JgM5@%=6$}++uqoGImhXaEp42G zLT}?PK3|+~_>9fkv7zmpvJGuPJ?#I4-m5#(!`u3$@>^z2YiwSNv6=7+TX&%UIM!d; zF}zJan>S*--YUkcoO>E}^(%{7_+H^X#OGq3H+wkRRoJSZ8T3nDzV{`Ohw1!U`HVeP zw!Jw98%wamG>Febi08n4mgIB3e%B0+d$bGh_dK2MXyT|2iARV8K2E*2yfU`p2Gb=U$B09u3-7PtrMN{Qs~3bTf1u{^3Oxv z`kXi&ZHN6Y@^rf-uE5% zMKNwe%;%w(YKjs?O}%jMb5eGWIRf*p9Nz_Ru)KlZ`dzb7pL4*o9QBw-p4;WT%sD9Z znwlH?;XO{fS+Enu{%S;jvi`RsAM1~2;7LE#_)_(R+uCkFy?+zuC*FUI&u}u+iu&?B zGOsn^ZD{jQXNY1CU8CBm?>Fxe`&rLxd0u(*FyG6&6=j9#rsi^t{k(*n8<;ME`tezZ zT##3o;`JG=zIVn!IFEi1FmK?!#`m0;epbBe@;&CqJeOLJ@xLy4 z(g(ZWWP0|Ieiu}z`liumjALKmy>ITDI|fBO+DYn$vyFTn(PwyV$@!c8lkc6=oZbPO zVYtV9@8~ofS35m92K^n+?)(kP%Xsgo^f}-efv4_ET|Yd7gLIfqqvIaLe21aa3cF;m z&-dB3qj4{pUiQVOIcE&w7|2K8avn*e&&n~+dFdzc`)yks<@uO{{q0{se?HN6JLf*k zL(?&iZbh4X+;4wJ<30KHjm@`WT+PLgDcE7B;u`;y*aU1SSlx?|>@=EoNE%)VDF5d28zNHN{ zJ+7+JH>^V$>JUp@*%b5Yo66wFyS1$hWqaOP81rX5i9UT5b7gZi#$GSn=U(-VTZ7c% zrV#IEynoxO(FeUM7dKVzV4iuH&%IaKE{?CWG5$F&@j3G<+ZmzHb@?otVFOW?4&ylI zJYoFhc#kt*+3Y67C6WK2>A2~PXInTHri!|r(dspFOk$ZLVe>O=bDI5#=N$JI8)#ld zzoBk(TG8$tKV2UTuMY!kvKqv`O4Tp!AAt@=x-yho4c-ynkD`B4Yn#f@H`Qo!tn%un zYSl)}^RM!GVBJl<(KhDi^UZiZ_v}+0&w9;J^M=(#NF(0KHUkY&Vas3kO zT8Z&MUVr*v_e11`IgxSZUXmZf2ck~;`M`(LtDFnRBb}dqc4v7R`k`!H3+Kt#FlMKx zWk39zW3B&iya%?ltvl$-rhM<9H~RIl?~N4Z5dA*D7;4q;0qE4c$@!h-={b-6z|xU8PxN`#t(LXTfYyOhkW&Xq34(*`^s|l93|hqYj??a?j|{Jp{>l{-dvPUV-7-F zb)EG)SM*5}>vln_F6&`cN6t~tVGg++eZqUnIfH$R^Rb(})clre+)F%DZ5O_eKHI@Q z!@PiZ1J>7YPtFqem*beSu)e=yY=alaej)NN#=L|)t@<7v^KZz7EC+db&(gJPML#m0 z^JgMu$4WlFo5g#o9Amz+v0x#}p~@Pl+_&r)QwP8`@?f9qX^t>bkHW z*j|P+jQz;IXCJdq9>W|;q4}6EdC&(I54gcQ>>hjQbzq+f}!P?MtL?!1pNb z2jXL?wJrI0kKmhD+$*MIdsz>*iD|Av`*=Tje;DRfHa2fUSg7uo?k~paK4E{YVH(Uk zZ+x)(nX)wI9n^OS+8awZG{ttTZz{taTfJjL8=mPm1LxkqhcU^%!27_|n|*sf(%E-S zQ`UP)zNhjb_wt$Nv$9TnlNG$7pL32?{hVW5Nm(K#<-hJei~70mNG`ZrKYx%5{)l-Z z<#C?bxa&?8&OXY1=SLqPo%&wCt&?=C-->YmIUhlPBjy@}Pw1-ykaN#!9R!{OAtUxn^=jdF0_Pw1JC?H6 zNz=CD9khN|KcszUHr5~TJwW-6aqQbreGlk)jbp(59`JcTQ6|T;7(aN2UW|E@&yju} zIffX=?;FnH_-01^_?@t!tsGnWvd%n|lScl?o7a2{`qcfNjPTuc zB*KF6J6eme_7UR8u=EGJ^C&a$Jtn@(@I3H+BISh*ZKF}v5b)&uxH!Ef2Wukz!}@Fc zA635~XhM1HLwqN_+SRkmX9n+`JzIFh`@07n zt=?@Ro*$octaorL4xkvsXWzZ|DU}!YUor2^Z3XWq$P@SUP0GN`*FuW0B*OSy^Z9n4 zS3cj{P>y}Bw4A4(uXm2uaqa3!v~#wKdtgeA@?fJs7Hy4Q0hUg3?WrsH)Q07&0w(EQ z#a;skb^R@^n9owNo(VkWeso&!Ao~;L5tx@X0AsT-6HtVkeb^q#8EJ^R zm@(lZB*IFOTmbhrJ;zZFX zBA)qC^fe@t_Fsd;MZNpb)Z*{(qy5**8b~@%x9G|Hj=4T&Nz#8>q0r>71T=D ze-%AzlrtVB#~n>52VUS>Dw6`z+lkXPn3TouQmz?Z2MLHvClPgK`Gz`Hb|H$B$>}Mmggf z^cm%h;370aD`zl+qntsnQqBmZFTy8(ZJ7kk$A<88qG)bF6;9__y#jY!ykC6l1oOHw(( zSa^++kR+@(mVDWb%}+kaTX?L>n@ARehhzWs9_Bq>#io-_FtJt6c$*zLUBymG5%o6I-!&^X#e#`On+8T1nwLI4fI;&j2G#(${BU+ z5v!a5S(x@;<@lMAY-Gi(at1Z99Q&`|VXRfoSW8w`IpaC@y;aWmAC~JVXDlW!M>*r0 zjIR>TBcO^kPxGnO#kQO;OHe<#Wr!6qc=u5t$LztZx9WB>I% zmfwYPMiGh-kL^%z;7u_;rcln{v#RaCRxT#wJ5=4peBl7 z8EP1d0@~@0qF@~Q7ry^SE}TvM6ykC}!TJq89LwZQXj?3pKBO9Ii)1roi5Hy22rqUF z|M3%CeJ_VnMG*N-2p!rV0^66+1phEG<`E=pDng5+%JWAuHn<@gHb-6i=MIO(*3qMp z%0D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܌WM5=^-%ZNR&7Q#ao=B{kEbl$jPaTI#aa6G|%gF@OKm4hz!{Q*WL_v_E z^H6KZ0t2~r18EMViSb^5<@guKMpASFjD4xv^)k~mnb%=?5KHWxT7v(HIFeVuq+VIl zdOyZ=j)|)ZrM`%W4A*fp8kd~}d*lngjfV@a%fFyUK`Q-J?pdz-q$pY2AlHbz=_%3G zvbw3<>AsoC@&4VWmgUZ8+2gcqdG2sjnSO6{CvS!-2Ia=H?D3jfom<8TJZy~gVL$tI+DlB9FGf0&GZ29>d$y;C#rf4?8$-x#zuAPw>4>&#dYGLF?; z#mmeZ$2ye6?Wele3iT}LdH zt7GMhBj(10z`1{C>KPGFf8GV`>%lgY8v9n|73@oBibC+&$9iN=t40uIV2Z&;*#nyIV5*UZUVIj z^Ei4HyXpZvRC9n721u3bSt%pejP;tyS`9DLT$IYg-!I65`g1H?d1t@g_E~ z;dU|bM(T+iD(S=$b|Cp5PYREWFPvS8f0grBRmItxpR0-oRmSbJKRT`|KDcuHoH14L z@w1nYtC|3N`0R7%EI+KeD(+u7ZfsRNWA^g+iJ&r`xjxEZJMzTgYe7e{84cVgXABrQ zcfjZ?!;eKDjW17aD!D9uY3U`Ijs4{PndJL#>>K;zS1a@`pQRo!&FWlS7V}Z!p|cY=oz`it@JVp@=}*~N=rz;0Ms-#3 zQKi5qY~lhl@lML@d#28;O&|#5X5;L&eAJzqoRYMmnG$X6Fty9tn;=`Y&X5yF z)an`7^T%9U=vf<|JczejT!DGrZE9a#zn0QT&wub9eQ87U>eY=H=SsNn-DUbx|1yGW zr3AbqE#MN*Ixx{>@jWmuW5U7V9&Lv?Hu>Jg55_F!l)K7D-pG_;Jv0lRlIwvRFS{Q6WZ#s*V(?d7$iL%7n8jjF`Nw#<%W+_R zM2;V^9zRnCi@{?cU?+1ygBfSOTJ!t&JnADy$NI<&n)=$X?AP}u>Kl$Y6HHn&BK-PZ z@%qPm6I0)1ST|Wo}Cs;KB^XRND!=AX(lCj(J~goaYX5%)Ea!f#b;I-2~e5 z5Tp^b!OPe{d%Crf?Lc1I+?ar+9*K*byXX{h*6^Xf3YQXOFdv|`C6hgP?n30-6zWs& zMmyA}zM{y7uptA%ml3h=a10~t6g)RXB|Y%odnxY?9Krm+af)0&+9xQ^Q(UN6tH|S; z`97?8qv94t_Lu43RD4R2uTmM`q4=sI-()hLZ-a=#6vrwatth-V(s_PmKE7ZlUaR;o zigzo1Q}Ic~=M-O36#4<>Q#i<2&%TPo6{{5&DxR%)p5j_XzAs{V;k|(mtNjDT7Zv|k zaRd%l=C4+qt9Yj33PrvUWIj5hBVMEUgd$%yGX6D1zG)=Q%i64u8mFS8}_^xde;d@jn7NyS?@X(=Q)G|{yepHT*pZA-&^5d zw;DWmo?P*~Z3Dm=$I3bT^L$DLD3fQIhd$X+hP(mtrSc!&R)#XiA!eq@1+med_&w99d_ERfNHF`kHQapG?GT-4IVw4hPa zg0LAzMhnLHb{4koc6m?krUgwsjMkEAK~smZW+S5=$Ww0OZuj%ZqB7bivB+sbX9FW& zMjIU_Yf_QQ{+LN>WwLY?YOFGG{A(;>jHBMT+vQhQoECHn`WIPuyL5RPO)#y_WSVui z%L!H^E2AC5K3Ey;BBogxZ3AnuGTMI3X=SvO*hYmi+RvgHnHKZ|OtUgtzLbxgyWRh0 zqLa~50NB&CpybkJT2L8TE2EvticdEyc@FEfGFtX!=cWa1z^3Q4prm5mw4ndWHi~7m zzh_g<-R@VBr}Jq+wQppMqdO8Kll>P~X=SwhGC3-6w;Ne^yGNr-k#)CQ#*SGTt=!F6 z8SNJ4v@+U**n*YO@)L=uz};?C;BGf6aJL&-ce{767VB>Jf7wETyWPmT+a8im87&`4WLi);0XP{gKP274SzvoJL(}Ets+MJB`85Vak+E1`@=Wcg8Q=N=f=InLucF*OeaWdNPFms`d z)|(dedA94^?apM`LK&?;E$F4Jt4Ky0dG2;U#M(R=ZJz!1-0j}W@}7+LlWfkD(Z0;` z-N|VAy>V}4v=JENC)uEr(avFicGlf4cGUxTteR;-YuU+nNJe|mfw;*D&qS}oIpHKU z%D{UQkivY%4^QItK3p*5VKF2(CA8do4{B%GeVK5&lx4Z4UfD%bwq~f5T`W8N)v00O zFInOYDG?5&AMk<9H;{GXHf#e@-4vro(h73?zsu;}RV(Y?bzXP*F!MGh7=0oLt7a`< z9`|2A{)F)xgkeqI$@m=WAdasFZYpSOuM8Is$e4J>RVVRt2b5nKz6Z>c>-(6Dm5adJ@eQuA%9YWqwQ%dMUhuT6HLD25aC4!E3h7uv65U5+~wHy;QJa=28+R6 z*&ra_V1ijJ=1@1|GK-6_)JNp_5$o|YWw02$gtu8!xuC&}^Et5n{@srH@`z*qy5mqs z<9+X;$YX+Os~Hh~eUE$nLl{#zMTdrzqgyrJ<(Qgh>*M|pCh ztS7=wTBG(7wRz0<$c=)KmTww?BbXm3^#O$&1y0lWS&E_wM*Ir3H!6NiQMgg0->$Z3 zf?+?R_74=FQ|wUutzrg4!hYDv2?mNL7$}-xplE`Dq6r3y zCKxE1VBoV_UNph5-%wjL!LUUW3=~Z;P&C0n(F6lU6ATnhFi1KheJw{AoJx^t~v1 zV35DABVGNYU!l(J>8b0tVl4Wng0D)MKcJqBa;?x)aWlUho6Xj?sM;Y0Bun zh*U7Tp9g@^B^}ufqk9i(>59=!T8KWuCUpW;FuHV%+$E#?6k1b8_dM2Y7~O+;!AKb0 ze?t~!boq%68Qs%>T`;;^nPeH=7FMHHS@tY1t@n)XMJPo^m*PCf=pMo$wT$j1OtXwG zr=xU??h)*RWpwM9W*Objuolbc=9tqmx<6xDA*1^%G$V}eSD0oQT_KPhquY<2af~iS z-90h72cte=bY)~MqdS)!vW)KaY}7Kk?90wEx_lK+MmNt6E&C43Ljx?**k6O8Vc*=@_{{*~SC6{9_dE`>V|34B zRc->+8Qdn0(cQ+(j?pb=&5qF(%GNQuJTiSo_chucqkB0k^BCPa|KKgR+|)E@=7w=qRpaAxXU~cA%TAsXah6uzCHtIl9<~YGtTP7WuM9sL zT^?^rT~=~w`jXO(8F@}+9;V}!EqUb`$wpu*WjbhN2;524&$?f{LqQN9Ip%qLM^%%2y#06m)A8ZUe|Y~j0s!gm9O?*%^-9ULC3lzQ^D10|i_->%^-9X{Hfx>qKh3^Im-whPL8z_7?Q21`3@ZCV+yMe-Y z1BLGf3f~PBz8fffH&FO)pzz&5;k$vtcLRm*1`6K|6uuiMd^b?|ZlLhpK;gTA!gm9O z?*%^-9X{Hfx>qKh3^Im-whPL8z_7?Q21`3@ZG=-xWIFJ3f~R< zoZ7;7!+un4;k#jz2xWfZyMe-Y1BLGf3f~PBz8fffH?UK_8}Y(-1Ni|z>pe?xIT8Et zd5WzX{}IJ26hEVQr{aBzj}oy@%JGPLc&efH7?(7um)BF;Q`E+5tpG1EI^)B6|7z&) zp6A9reBwlr3io1e+*kNP7lv}8K;mngJaJT==!5&iZ#B7MUmLum&UcR=X(#%9n>yb@ zKhnW6?nB8Yl8b*{mT~OE3gv`*9%9rN^n=Lhd_kv?MSVek4U6yveGve@pud*Fy+h&) z`W)<@#lE1qE2uB%TTo$FzMzxFVmKTo5Bwww(HHbrS$OCK{3q?lyKv7J^!;c}eL*i_ z&BhlrmA&c<`X+X$RD3~`jG-@R@&aA>g8m+ptS{&#tVXS}?1^4l?|nfxq7;2W^BU!R zK_82ObH1Q2Vw&{@eJw_=o&$AZm3mUi5L07(@$1tb$1^q$x#rlG#a@qNU<`cK) z3%U+jd*KV34EVdt7c^PffWDw_;AU{Xpx@7taK4~dGS!%w+)>;T#w<&`Jv(2}ygT-M zL9b_K=L`Bm*6e&i<0_0pOnnGX&V8AgoiFI&%r3ny4#(0Q6?57|hl zk_Kx_Dw2Ejq}o)ss+Qtk&#Nl?Co5AceqAqUUAYDd+FJD`yL?qU ztiRhNx&PGD;`Z$zv0j$-2yt$4k<#2w=fd##+tx_;g~r9Tb-|oC{e7Fft92IL+*I3m z;mS3&!d(B~gprjKVjVAV}j%P`ns;Mu6`wE zWWZtxl6RJc75C)-pbRWrbYQJUwGv4Gr?RjiaFS;z$IjmiPt2SXFl4z>!yvH zAqfi=4KR)2F#MRmr+n4OW6EGLV128+2(vh?DBnX!;MLFslkYf0c+tTPSdTn9&-LIt zN3$M$TVu*#F*p?T(nc@BES`)szrOo1P-a|+roMVCn=)7o?#9J^G#4~5AHzBL`Sm@7 z`tTZ7`Ui!Et*;HsetplPzC7YgFlo(*@aucp>mTngO#d#!vMIwl-sqyf4M_9rd)Dh8 z=u=zY^;l+o+W%Bva1dbbZxO({qn<;*blUq2s*-U2mV7|9A z-|je+=RBXsD4w7=Pm$*erq?R+n4$e)#Tym3DBi93O~t1apH&pD5#?W1`!9;(cNp=* zFzl>KeLUQ-mm!?4BgFmM=lf2ND%04RQkf#P=GQ-9n+;fvIWCb8SbEe+K#HZ^vgk2fp?K;>vHPA7W6sr$WDa zMF)1%JHz4E{VK?BMEs9B#-Yx~AHIe0{U1M^bs#?2igqx+?aV~u8X}*xjXYuX`faQy z%(LwL?Pnn_$Y0xmJTG3c<(7`4Q6K#rC-r}{W7WmCbl~o{f76$T4lm^l2F5@>MIwHs0YhA60BU-B$9w&gdX?Nhg-pE2g=m3e*5 zdIws@Tp!&*=c=&bq;hOWnM%G!q3GK0+n(cR?e;?F#*oT*3 zx2WBM_Smjyn6=%lX!9gV=Q!NlF)@$&lJ(z~jOmpfpF*A?$P?x--o|Ye|-b ze-U_Z*j3=wKaU@Fd-(zO_>(Tdo2|FwPkPqCec;}3D(-}Eneg?-L!Gi%yh+3K#K}8& zlFqyA?=542;Op3DaW+Coaf=0u6C=17%c8-E4nd3)B2)LGLgdR(07B%600@z9Lqf3- z`3@9S*V6Rm+LaJFX(8GVyN)MBCUxV4$oJA3AvZ-8sUWqGX2sR)Ha%%tAu<^^6(Y}; ze(&{ObdTp-3X$depw3-O-+&5@^f;4!jfJcb`HP;_dm-`&G)f`zVJvN3OaF%LSRwM6 zOtV5{`8>(GmVOrHoe=puw5$;MG1g**$P;K;Au?IlA|W#0g^Lh*2?opwkzoX$YiX(h zJt6Yv*tgl=N1lX$rZoLYSp7EOA0{JS2m1y5M2IXSYlX;b*&!=L{vTFkg~;+={>-h2 z)5(ZgZ-$emk}}lFIT6_TkE`-S2 z*q#+4lZ13aW*we*!tbwcDVOm#wJd1!J%F2j7xo7 zA(Ih*4<$HbW0LgeR|>V(K=G1Uo?mvjBjj_&0k zV=`jCfAfUMLphetwKTt2@LfxDW(8k}d^poR*V6yR;+_z>l(r{CeuB0qME(X__gqVV zk9K!LZ{^?!$i$k8;6P?$c5Q^16GVsyjfMq`y3 zc)J6p`2$!Ck6~nZ`jF=dQzY}EA>8Z3V?`-^+E9L!5Wbh6SCzy#FoI#ttMKl9DkH)g zkkF^1fkCtmzKYxA;8^6wVV>V}hjKd+Cc5GLdKMWg4QkA<*^_a8%&}~((vP{Ad8(wj zq*Snm?ff6`O3g>&x1l>eHXL7HdFeN1RgJ6q>72N#GG4Ikjd<_ORmW?d*XG{)EMS=2q1?G5Uh-3F6zIZ`f#b4WZY#y0LY6 zV^hQ0fa2YTzQ|{-o9G_qM`i=(W2Wh*<@ueQC0U2Nwtz~s9I>l$&t%E?&;g#O>C&N- zA#SCFHkm9rnIpLqz8gc$eYN02ivm4&@?i#E*u(WU=h(d&VS@Q3)er7M>vGZQ)&=W|b)KBsp6%;}5fFPL@O%-V%2vu;^c z*I+!OCta86m|3-WnMQ@lL8tEFopio!4Vv{PE&d0auDIyihj(J~;#EgZtdq}bFo-;# zd0*FbK4-C>4Q0cwY+K6Nl?$gXZiWU;xBJq%waE1F7A|{_pd0p36cOBwt$yC z374W3h~w*OUTT>S7r9`J+PE>2a0RYTyndQs>Kla!T-$888}+d~>tk6{A2pDSH}&1% zUF{HU-(T{5xnA-aEpIA5<3*Uo6N>UpKtsGjn_%)yLxdL{+>ie8set_hEn)k28kS8N zECzo74^1A<1hdHd0l&WL;ck5yZ`QXS%Pcb^Umv9D2 z4D&I(A3wj{$!HhvsAWC4T}-=|Vwv@t@Dl3dx|v|o)+55N?+B}$Tyii5eM2-+8_pJ5V4P*L>v?ZbBWj& zYlz6VP?5(rZE$6H+nAio$qREG`OY3ilXv$-MDl|-;t0iR#bXqwDb7+nTXBWrM#YaQ z$~P3K?{>BCQ+!162a3-rb}0TBVOTUOXDam;W|IoC=~a`L1j^AdWV>-p8hrHB1CV?SJnQsn7_Pj+-WFLNfFJr3V@#=dkrH4-dw5?`Y6 z@RE%cvhx_DMCo3ZO2*|jg!gP_={G!>HLwcF{Vib)BkS_g*LK<7K^C!lc`mnM4nQVl zpP6ZIW@44)nymz$xsh$3TyO}k(fj7m;vr?sFt7w|Vl%u)Ag68ZD!<-=Kf$u)(Ul2z zlPt^5hz*TPGj@w)qZ8>gBczk;h)?toU;hS?snX&6PmVNIl3EM^1AGQ)jK9U{sJ0JnDCgN)WxGl zBfTsYW1y7@Pkj_w)Fn5)bCU_L0v5UCPU=r(zs97)5-XFPh5Hm^m1SkFPEG4Q6TSd7 zk_peDf6;ie9*2amCYV--F>Ru0bus&JL}Ha?2e1!ECRTa&Xr@g{tU+1fLiyZ*u^F8G z9n+3VtRdMOLRd#9Rz4^ngC_U}U6}A?OtUVzC$k0TlKWvMI+xt*S!)kW_#4P0Ot_4! zWx_|X;?t8Zq_VSEuVup7mz{ITy$GvGCj3t~@PeR(3WYO-GsVOb{p3R*vWm^msI z2dT5TpiDRm41ALR$iWPxiSa%b%c(TBe<2g@GLtZCj2fYdrbI7 zR^~C`YgwDegy)&QBNNVxgwKTk3$h6lE++uTg#VSr`^hGZDpI*>7VCxy|0Q}WO!!uI z+cDwSu$*JUuVk%VGvTS9a3wsK+y}C@(;{9wxDWgX(YfTlh?P4g{5YmMCY+8Rz=RK% zvM1%}w!&k=zs6|CgyS;qGvN=jtaHhID9bt~d=xV;iTdMqFs@h^<}Re|x#T{O{i>Ii z*9us~$%X>_?Fzp@_{sWfph6zv6!PU+%;qtM)W5SPQe|liTxe4U3 z9U2V27AyWLcGUyq>y#DIGdOGy5NB9@W5T(KaEkaos@*&F4gAm0*D=53))U1+;gMLw zFcb0w!N5CP@ZU}BxS^|qLun-swYx?@EjP{YDgiZLjwHA8eP@*qoP%%wg$m{yCtvGK z+XRlVytj~u{&@1>HW5upGAxY8`(KPW0}c=kENFsHMVaYJSJ#6|Gp=ZB+rVR4o5baSkX&PupGUfEm^XU|Qm>sEpI!6d*K>QyVr z1NF*DwD&G-=N(4ZTF$xisfk;f+raki4p-3LPPS`(LHCXQm))H+6Pp9C6N;A%u?XlJ znw!sCiCwoIGFb4&tC|6cx_Wut>eY3^0b?nsZEtFCYg}5(Zw5dNu30Mk0f}Aky-U?0 zc*6E3IfD!E7$M^{w9V)X?7Aimx5M0L$8(0$-aaYYixwO0n2ZLRbEXG-#tm1{ZBS( zTaLhR&`E*McXG&hz?c;lo)jaVoSQ(Tx2|bkU2je^%U0FViF&*GIN-UZwoP=?e^1`@ zA6mrRnTFXv^_vSqF-LkA-p}JgBJZwny_O3HZQfJS#;uNo8Hxs&#&8&Z%->VKYUD9x zuo$quv%LtjIISq(79{X$XM)Mcn@cY`xCiUOTXepI<4pJa;^!n#>q~tzXhTD(fSk%Xl zEKOhNB=MKYr&qO>dx3VQ)0d{yiVp4ivY?MEbp9 zecY2=*m=$}d0A{~ActvKOpX~jNZxarz&QVo7gQmLL;v=`yW>Gtu1ka_cs%yV%k8E2 zw?J+e`azwjD7+kOo*Nj?W0iQe;tIu$iXT(FQSo*~;pI^75w(SvgZ-S^^5O^fZ`BrF z4z}=eK;h+p!pi}Lmjenf2NYfoD6f8iH)3|1I3RrQ2ZzZ#g8)ZYg%6XD8m*%%D^%l5Uf|8>VZe9Esu_{ z#g8&@iN-f6iXUaf-=p>;isDBZ@xsdi3_Ji_Hrp3J%0Tg>3=}`gK=Gpt6hF#9@uLjfpykAmGVIT(Eq;_? zKdQF)QHBlit+bQE1&{4bR1`nTu*HuuQ2ZzZf2rxh%V8fg{*>R;_zLdNh!b7? zVr_;K%Omu}i%lATWHs~Ud*RnO!#7H}9Ppfik;Jz}1A~^S<9;1Nn;bx+98QH!8Lpwj zphfhzVer_CC8`DP4}I(#GKcTx?LqZp9n=W7OieV$>0sdtVX!XxBhopqZW+0@hoIwy zwzw=Q|1w+L(MK}4wLP4h&a&GFAIY9RmFiW`G;`@XPEH(lb1Nt(w@Q{1ry1d+JUc2U z$1|pKauc!hf|GjzS;5JD0;f2`$$g(Gj*}B2F5%?<3n^W3a!DzD!QcO0RtxvY*P~v? z$=ytA#6qOu9d8g%p(*Orn2swgCl~YlDAVMoelL9yb?O~hBqz5GrMlqc)-g$Ww@mgk zC}6CzEXhY>^`4XaE=rM;dzO8+oE$x8I!>;XHCs;ZQ|!FuO?*FkG$H~>OZ#{5wm!em~$;rrCPOgqM zot|_dmA#%7Sx%09**Q+G619<&3t7@~a=0nqk&~l_IdYubi7x13xv``HC2CoWYiC)dpOEGH+l zl;z~kU_TuvC-*%)b8`QI^?7%3a+}a2;p9XJ>^QkO93{iLrE=s4t(nPf;mDZOvfNqR zOpcSQ=6E|!ZZOL_PHq`9J5KIO4y5Dc&S$Dqr=Gx6$I0hn0cPcyUQJGn z*sD|6LRd4j4gVx|VQ|nA)|Fvh4N@2D;k^^;lHzreu_hRHEJw!@f0Jni`||&;A^yaf z>bs8kYr}`rSjU8hSi!eYwVo4XPDfQKvFQEM#${N>o`AbZ^4aT!2INuYbAWtiGJ(S{6VYB8T@r6h+4n+>XTA#jr1qd08;*KTI+|<4v{~FK zxQGm$vxMf3nCS&?`wIz&ga_R+;Uth&DkjW`WhaG~0R7@iLI0W5 zltMWsyo=$d3_DcBWm&!wv{X9k+xo6ehScMdm-75Hs;FZoUq{U^rUbkb92JEMQF&o*IKF?yVD-I7(9*( zJatxnPCAN&1y(+RJCUX(1Fe$&_;d?EKW7eeuYyuwEKz2P`= zPjX>b@P*v=3=8oiUx?wff&0gB{Fugb2GidIG=b@ITZ+rAa*5`;4&-$gx2}xAXEUo? z+FKiI`2>R(j`$71{<0J}6qRs&dp%z(_DA`S4mgJ8fD;w>@QlfL?BN-caoEE%Cgbq; z<{3r*hvQUuMxgMEe9*-OiTvWRM8uz@_FTm}jpuY{o$-*>JnnHyl;a=u@IIgmyMW+`(gQ;9dA{KNMEphv7{|P+Jb@L zH1TQGg6*%G#1l{bxULJ(aexS)^U|#qTroV7yT8soS4KpB_i~6nNeasdvHl|3n(%UlxDI=W&B$phV@Cz2jM%ZH60*k56o>dI!uQ-;TF8B7676HZ9USz& zj=c-JI06+XLGWYwpUl@DVnl=JBK*VjYJ3QgcYcT}iSA@dX`Ilb(Pv?$N+XOjr;WXo zP#PV|$P$zGh-68n)&Qdi@h{Aj{Dgol6@-(*V*w0oKRi1<2#etnl3`)`$25+7p8pr6 zC%BaPr45iwhLc$~Tv*x?ghw@T@vI<~;zBBg%^hU8*zdoXaJVF^Z@bSUCQ9Z%*kx}c z;eIT+b4gAM@*dE>+B-5FKX>e*;}4y9X*6Qc#Q37hiJ1#lRn0l+h!vT;t2bPHNmYE- z`gq{_%r)^|%kc6qK6lwToOM9_@xHvPcYho&|J@%)J=var#?lAyTFK00S)*7YtKwem33>@w63ggUjuJXy$zjx zqwMd6X5TD+=WtKfJ!|r`#e9738HFEhT$3b_sqh2S7!Jda`FqO8`vFr1i{RsfU>z4U zn6zoI%`%fVAs>%I6HHq1JBP=x9*-jvPkOk%*!7r?Wm5)=!FqJ`4lZagX(z+REsYIV z;Do{TVtqt2F7;S8Ww02GMZ#DvXkb2u;&%?$qCPxMNqszBoBE31IV?s(B`eTi+A4nM zaI4opEZX`m!?G#EDlSERc`j%$X&Yes>wA~izX3=y{ktB^EW_=`@CEkIR05le{B2PL zNceF{@7_6-VPo~?ox`EX_kgJ!Xu3eeE@Hz%3@!znJ-u_-i2bFrakdrdf@z25?s(^b zTWv3dvd=tiJ7E_j&pYwXVW76ob4O?2D33%s_YMB`3I6bwyENJt4el~WzecNDyvszg zZTNnH>lEQV5|7mg`wI8rF8vB&P=+!cI6`rZBKJ$iPg0zzI9G9j;+cwdiYpY`6)#r2 zf`}dJD#cG}{H==rrudTL|07~ORuNH-=L?sPM%!+;q2Rf z!2Xf3*7LN?CY-E?uJE2`mHG2_)w7DfXY<~Iq4-&43GUf=FV1T$4j4K8(H^C?9Ew<; zsy~1PQ(y73$~sLqz&aUtPhpm6ufvb$I}=RWG(?zXrcsl@quB(rcpM`9d+ili4;JKl z@KVkGaXpHkRj$Jcf>$IH%;L$2D5wvEXU2tS>Z`}HDZ@H$#Q^2Gpuvm_FK~W+EvS$8 zDeNDv#_1d%1^QU79*97_>40FTWcpH24GXA>Zgg$OuC zk>?TG@@xW}srFn&c{V}3JevUPG=7C*v*JaHmnc4=_^jfu6kk{To1)y!qF(XQ13U^t z!2ZlstRdnYvrw^4<5v^0{%^4!q;tD+-p#>k%edg5LGH8qOmo3bY0Rp3;+cjA2zKj< z0?9K?YU{X;vZv=^F3Qa>q%e16D){)eFu!^G-`;xbHzBzl&S4pRwxkU6Sf;jq_C@+= z?9;Q$|9St6UGOEG>Ch90Q7blp4kTv{l!ve?oXD{IaI;~LO1MDc&tZjEAthRke{SMJ zKANOUN8^9=ugHR@tQG(iVzaO_7ED~28c})-vZeAT;eY8VtYtW_PC;e^{(Tg`eq4x6 z3lZHR8+VPVitHDWm>McA!CfOCr&1#p;yXisFG-q+!uz z%u*`SurFhAa6BeEmZ|>4g|SXfn95RAWUNe)9l2Bi`b5xxbR6@M<(lEo}EO? zPFzU0i?N-!kh6WocH+WYnRawiE}uxl)}xtB7*Eeqp&eUk*wI`|=dST7COT=@M_6kQ z(l9=yUlqoSl99F2uo0}-PF%=SR&1qV>`S~;6BpKTgyY8S>nv%dVb`;b;)x4?!lvBB zg`CsxtoRE9YFmQ%HqL>Fhx-BerY62U8J-l~HO6-&MkaeXtF+RvhgnyFyTFVbf??ca66)r;jD4g~8s;*KyGG4?F8a}vyGG6G+%?*&&|0tEHMXH8xQcx$cM=EJ zOcCM@0i(5T=+|_f|G`Ql9`<}j7MhN9<5`h zipQhe{j@!4*mbPTlZIWyjGi=XEYo+ChUHj|Ck^`wvX#Z9VNOo1P8v3V`P{^X{DL&@ zMjG}CMrlAiBFypeB6iZSn_13D!){@xx|W7T&tN5tyT*7$l;a$tv75N?CoJxyVVAOU zCk>m;ROhZS!?JGT!c)0voHUHLcd?U(k=cu#G>o#u*h#|b0x!@kD+o;2(tHs?vh{*&dqk%qycB~GxV8BXEOqgpOvr9tLU z-`+pie?IJ2oAxYE@PJ%BHHy$}0I5prN6vu66aersk%` zagG0Z>QNvS6K*JjmC4_{BydnVw>m%d= zsK^b9)>_fAwXLnTO(|`?iW-$#YmKF?w$y0vOw!}4wzbyV*X^zU?{`0DPJ#%v-nReG zuLmY;ul-ni?Z???pR?9pYi0Gi$oQhtqSA=|q|0}>tBb_9SaGC8evH-U#4yl_b7BZ1 zhddpI!BJ<%5R1JeCMBlC1!lQ(ZzLiVMEH*A{a(v=9({4}rf2$uibyuXqO>s{!~A=U z85t5eKS&XTMfJr4W61*_NzY~4WZeIywMfC_3iY+L$GM{9p%LL1Ogms(wRsyq@ye4X zFq$Whe${IubHWvza?OU1p-=}|v0)``Hzxf7>H|>f2+E0viKGv?q?5HChs(54Jz@Os zL~D^=&6dB_a%2y8bSF5VV};TmSwb7OQ{g*b1P{7~QH3waL& z=cwdBZr-$_cKf=mE1ni9D5gNMX6L$EneO+lWm1#M`iLQxCs+|2#)}AEbZfUHtI`5?61ThJyzR zAN+jY;HjVZmOWbG;(3MeTKv2h^YZE~(>Pf^ev8y)SdRHNcp`LgYOj3M1L2cSUpilJ znZ_ix1C3-m_+60gWxIOIG*a;`dxs{1bRFz1(BwcWE$O|cfW3=qDPwdd1xbO?1oU{bYvP&Am8c8G+sr%(~@ZnL_dQe>R;hgv*;K0 zJKL(~GJ-}9<_oeWF5NGQ2o>A!hl~QZgSblZmof_WmA*t+ zJ!v7N5PLWr)5aD+ev8jV2JsCZ@+Qu~lj=_)aVXOPM=Izjc&>uH@0mVZ!OIj}t6;N& zH!FCTf)6P8Z3UlFkneG%_p*Yng1=HwsEZ(7SaSl*Lsv7uu>S~Ht#Ez;X1uWf2)I|_ z!u})h2NeD-1&=EDBL!Urf32Xf|A=zJ{v)8U_yQ=-WdRENkATAdBj74a)-UWo0{@)C zh5bk1|EX|c{}H&b{|K0Y$xgn79VEbs3ZJf^u>XkoDuoOCkHCffM?hi!5%4LM{&NNS zHG_18{YOAy{}C`7YXaki{YOAy{}E8we*_fv9|8ZM^5Lk1$S>?aB3{^k1Qh480H>;S zVgC_$mBNMnN8rN#BcQPV2q^470t)+&fWrPGps@c4DC|E13j2?M!u}(mu>S}s>^}ku z`;UOa{v)8U{|G4TKLQH-kATAdBcQPV2q^470t)+&fWrPGps@c4DC|E13j2?M!u}(m zu>S}s>^}ku`;UO)To$0P{|G4TKLQH-kAO|7vj2qrN8q1RxUl~S{Aq;?`;WkRpoZfk z>^}ku`;UOru;4IW*nb2R_8$Soxh%l9Rk}Er1w0iCDP?Wx3JxPg{94X$#Pj(^e~qV< z)om22Oq(M@r|>lFL6(-Bwmrx@`-2HIm1`QG&caU`WOX^T-x#{h^+9HLZD?l~<#axe zY>v7AEuX`^OkK%zWxg>d;SUaX;7M#b3f)Q=yp+z{a!+==X=u;mO()uN{0=7*M?nef z@l(p5x}H>HUG%eZ=k4O3hd~N z)PE9zovgt+4-~Y(&Oa)hRuBCNfn6uDkqbdx2<+;KN`c*BEN^}-u$v0RCk1x%1>Zto z$0tCJZ%q0irg{Z-zJ$Q;+aRd1Kzc6As1H`5XVyJ<}8A+YoH5ZL*82<&_bft}6z5(2x& z$Y&3Moi8D}K1^WM50=p-`I8HEj4EP^* zby8sWY4(Jc(D?@3>_r0u-eb?`)QkbQv20RcS3;6Wf!z|8O$zJ|GILU3M=cs(QegKL zqDg_Bu!x%!*nNrhCIxm6v!bNHjusGoNr7EChbSqq`v%J<1$Nw(`jP@W-kCm+z>Y7o zK99ie%WQ;4U{^|N9)aB;;wK91HjpKc!0tAX$?!p7CzjTvz-}|EPYUcRSnNXtc4NU^ zrVj$UXP7Z5uscdJNr4?DF1`;D*a;(p%9f)K0y}yd^Cbm#!-*yZc9d%Sk^;M*Ff}Q# zquWkjQegKRrX~e;HB3zk>~@juq`(fFGsBk@*!_rTQeYP)DUZPJR?_ka?8=$$5!ms2 zjL##mdz$1t0=uV~?h)8+XSzpVx10Ef3GD7fhSL?;`5>@+iwq|PcC_2+`&$Ken5u(# zuVR>l!0u@_`u|E`_Xji(0=qL%pS|%MAVV6k?SVD7JZogS@bWf_`yQxsq<5k*R-VLs z>4OzElDm`00PDA`ia-hj*5{O=2_#*$^uw4GRwtauDc@?C!XDRjSn^<2i{9?ESx2lJD>u*;ThbAo@;axy zxCyCY!tb4uzH2X#Qe?<>UoARfwRBLZ1i_N`gdkCL4@KtE!(I>Z{OV2O*qR{>QL=jl zIW3vQ2j--Hg64b`dRe&F)3pQE*P?y@woh49U0-h$)znsRTeD(ib@i6otBj(gY?3G5 zy!gMitkU~P!G1+tl!M`6l(=f}QIRqUeq-eGjrp+mIN|)&a>RKr=H=B}R(Y~~luhU| zEXRDOW1sS8D8whIzBG;AvdXP!2kk7d9UmsE{49hsO_~Vu>R@kK<@3<|VLy-_q3)Mz zPZBxRNCZ?a0rwqg&SxF%t~6eO@DJx`Pf>UxO$BV?65rIDtA?iThN1v80O zOYHwbcFDft#W_vcCAu?lF)f_HWWTCyE%b&U&BVbg^$?Z-s`Fk;l)RT{Cn68<@39l% zusk|O$TkKq2P@6mm{LA?E}Xa!x=Y=L8gTPCy~&1Qc>kKq2P@6mm{LA?E}X za!x=Y=L8gTPCy~&1pK$3J2^l8?dMMR!#~>3owh$IT^ z6!s{AFIISsf?E{arQm)-Ec&-Ac(;mwRKa>I+Dzwqb6R#=#CzY0E&80}DeYA{6DCR# z>#6Owgt-~PqoY)Sq}-He8YwgFEie5*S*iAWmo$OsJJAkHLRLsSEb7P}4X>o0OmrCN zDLPE+dGIf&&9J0P6N$z}>{Zzq@leEuL0sH*nDg*QJB8zN>hs$N0Rn#eII&a>#-OMf zu(_fl@@$gy+f!uZ9}o#cE$6UYs{J4UKIx4_n!S-vBYzM<`AIKjF$<0w!2&S9(w|tK z#C)Q^t~ z45-XS<%Z!aq@0%Z|?X#f--FaY@p;otUCK0f;?-bt%pxq839w_}Cp z3e0C6ZiK{p>S%@Qcb-b%-ivv8^?rXpSw8N$bs3gpKJKISrPHP&LWi057Jj@-^`(Qo z-`}4_JN}4FY{!Sazf;X5Qxid69qj%7-j4Tt-7kdfKf-EH83c_v=%9QC)TR5m_xt;^ zpcg_O@&{HE^m^+WZv{Q-jq6ME*ZckbxQD+Fdw-{-I;@ESYI;owW4n{MVjliJ?EU>4 zphpLT`eKm3`xMmiA!sH|(ugPR%Ls!+olcsCe5dpNz8d)+(S3*=YuY9{6FITonx82ytT96ICGNM$DwQ7N0H-N!S{81)<|5OuZc&ad_zw> zJ63F5j}?21&@uifI>%!`nmUS(f@dqp`$2 z593*Qcks2|>IT~9bY&Q~xQ20?oAGQkZWxzz2b^2n0*2deg~r(}uCL)X_dG0HX~yz+ zMmYNQv~cu3(mCw>FmB~X<0F?w9vnNI4qt-^_>F~wv55xY7u`9;+leRGG4mWl+ z!X^vh;i{&1Tli?a5q8|$LNCTs!d0=IpqCP!+I1-WWW3FJG2Y~We$~@)6L#zmI}Pzv z(9AFnCT!0eXp6t?=2%*5ZR|Sm#yZG*V+{31_r;4h*bdSfg+8Fo`V-TZcH>#_mZtMTH1IXK zTl5ELOay-c_MH>`dccX^$9UrVjOG~m8ioEl6L}6h$Ixd7U2k9djfR-)ON_&*`V(z$ zYe#=Nn__$0(Vu9`A?JDY^^2nK{0&QEjcBKh{y1Fql<3pL#y*pF^)1oQ`{Eog;R6Bv zdlq;Km9+{R{sHHp>kHq8ew!is$1;}2!MFKCVQ)X$oduoUcP^Gb+5^S{^rLL&VdG`y zLx0xBj6gIV#{AH8<8YO%5BzjvYy_X=kK@2TIOIGP_n}_S5x>(U=18ESHs-_pJY4l; ze6O zqK!M-_r`?m&eV}&hD(xQM48F4Relu6jF?+>X${`Mj7ZKUd0VDDi?NP?Fxos z^pmiJb(`JjPx8PzVwgXuC%)6!7qj5Q;86Pl()=;r-{Ya!oo>L{^Ah$9F9)0h3=1C= zhuU9=A9h}dTeu(E!Y^R#?m!>>Jl^P(i80+7+K0ZueZqDcWnGQoeX+yFQ}Hb4ICxmt z&AFZBJnMqTzOaGR7h^iaXDlM$eHll5l5+>fa$46U__#38|L;tZW2bpN6#g;BY<@TV zH3Wa4`uV@v&ljSf+xNSTG4yRE`n4(=KN7kJ>q4t*g;&PHtp{DPCd`V)(~O02y8(R| zMHpj{fj+?Jndc5UKL+0mx)FxT{X*Oe#^7BJ=oq)f1L)ff%z5^|o{Qkw#FB;=wOIE% z7fL>l827}*7$L7HQ?*a5HAXw<+H&~5kn=2PMC0#Z3=W5WlAwn<61QUTXHi!Z^EK=t zPr*=otDE86LY|%4lzl`M?Lqob%~WXnB$sv=`3~QU77*m3W(h`q4KH$cta!-+*@u zcB?XCjWMig!6VMCXdl-!BY4R9xd^|;``-U{)+WsR zuxx`Kn+uS~KpWB=uJ_S+FuW&*`JY%raBsYvfw`8BKEr-+aE^JvJp`Je&>e0Nv;r7E zJG4`bT`F)3G|0DJOPHVQQLwHthBb0t zh@;Z2P_&Lj zkHrs_y%OhIY&1mK9%u(vc27XMiN$n71s;anC8n`tR&^qu>L)xhDFb^91)scm4?Q z+_weurNzAy>6{mz0%SSwJh>Hd;y$gyeTq53HH-JD6`Im@sQoA6UNv*jkF6VGW?)`7 z?c7;W%$)|_uPmfV(svwtar{~Z4|ul zzULaEc_J@IKtu0CFz23QpZ($k`i#$huD$Gw9JCAbHO{%udx~_v3_2W3)Emz&zI}k< zG~*ZT?=zaQ?%|&5wMOQFP6qZDMtii|j-qc`(f@wC`$1)_Uh?zaqk_ZvRvBJ35P!dikg$oY|~ z`zh3YtJr^W?~>7e@OHct-b6l)_T2+~?YsSk5$(=HAL_C9p>DSGNa&}6mS`vL_WQ_I(*N@dnrZ3h17${Daa7d_~qmiWILrB$a2U^vS6}PiaURGAi?in7vZ8kv;(V z&&rsMBW@XfgWwD-!A2&@*qNG8ABFnmiL8)`ROanF0!1QVre9C{_x(>McgcdF#bZ(t zr!_T46jlxF>7XjRr~Sp8WGYUMd1~;J|Gs8FvkWu_s~Y^$XyXu7gF zbAF8Ia8WJazlJ|%O3FfHvq$rb zLP`m;+UGJc^=bSG_|FS`2T}fWD}X^}6nKu<_agoVS<3I@ z-}LwJ$Gi=HG8oEW86^D~f6wsy{tF<57PqYP*}P2JS*0F8j&*)Od?T46EU%)*?;@u) zRbsxtdgQUDNh}b!k7?6chczlAa39lVTq>|o;0q)^bEUwtP_c>B6xvm@E~a9E-$x>S z4x+zSA>cEu`GN1T^A?N{MU24N#1=}-7x*->MY5_uKpb^mEYmUq_p+)b5(`1$8Q4;Z zWl^ujur85UPT&GkSthaE01bj!mr5)g_<{-SGKu8{W)izxV)=nW=3F7Mg1~u9tCE-# zc$YO)ORO{?4qdO3*rdQid3`e|_W3P>DM)(7v^MrzTeHQmHZeD+%Syt{q+^e2wgO*A zcB@XJd#u2A(y0F*k-FECBeqqhwp#sQAk5k(u>)4%F&4dwgKLkv(+UV3k*g(oHhlyX_%RZvU0M9y_1@DMVjER&coz}qP0Qi%lu-(=3^ z93I>UMhZ3XQ!b<1>HZ~nQkh>zf+>%nB4oUQWb?AD0#s|RUUMn?`iaXB5vfiF}te zm`m778JuqnT7@}p4rA&-Q*glA*OIHt28+J!YYG;Y4HkXd&y>{;*;KRLF#C%V=Helu ztFuft-CQu3%d0s+rd^Utp0Z6cH58#7nQ8g36lNMEGc6g0ow;ESHU+l}hn>gX8zOTq zH3t2FJXSHsg{H_t{%B`F^EgBDxNt~y9jcgVa%nUd4HF#Ck{r()NG z#D=0f$|WBQjPRCJNHJOc^Rhcw%^t+*s=lYHlHKu~B))hEbj1zx`;yAiA@#NJz^JfG zM8zGl;zhY&;sw#Lq1(yO4#@&!t1f>{r-A*#?Cz%iW6RT_u`ogntT_pJfYkEKvu!{RMUon&bYKm}Cnj z$61z|WTLVG)(G;rOmaNX5*({O&K7Cbenc)GvceX~Y2lN+ zEH?&CWCuLYw#|}NkUaNKx{5Mc#o{5G(Vi)?hD(OP%cMC~B1;GJkexYQU|9bK-$!aQ zL=E=-XN|#E5}#>tg*F!q*}`dgp(W~FGVEav@@z?H`B08lx$KLDqGKv#%Ce#2-kxJU z4I0+|oS|agXf!lz2T9MBHC#F@$_~3ocH-r{u~c}GoGA*c#OlY#gYipnZyDpj$j77u zQ(wDs%c`}1-?_HP#+9qq(hS*2G&?@Z=`^q4NGQfI0+ue(@`QOybY0>I8v3QCdWAS+ zB6HQPTthcS$d5+{a^hheH_TEyt;oKz$nJx`<#w=WJdyOG5@LwErpSpDJ8NcFtZ9i< zK-1>YH5F?L?HxshDCR^WvmfVL=5G*<$&(hin8c6QNE z17%nIG-6jZ&WB87!c0SV77x7!WY_KHzXr@4yvjH0m@jV%HDjxC&vGzTQ1dtk<<_us&{JX- zU{E5p4*J#JSY!_HRx6i!Uy2-xLx5vLbWmjrTpgC=6<|M5P!#rzo4@}R@*=O#7(f(b&7JqY!5mq#~ zcgYdy;eF;!){XWa-){e|l$O-yKteXoy+aiFr)OsDGh3`?yD~GR`QXF8L6rXhKB_YF zbv=CG<_msLtVh%0Bd+vhv^HcqT2{~J$}Gu`Xck%0H26TkYYh8kdgiPXyXIz;6N`ay z9;ncU`U;AtaZ;_mcFR`dd^A-?)YNXM-CApKx4B})Y8+%^wz`$r*>nG=qG<4S1s58F zn;=;bkBn_wHW_?=%LqITaHMRdP}`L;^>rI?P>&afNwNb@)TO@%5M3pRuid(`+Ss&l zWnH~dy@MWc>&0ivo&07o>sWWOsL)VMH zq6{_j7Rs?{5yomaPlPw#{(AM0DYf*O2`K2b|hjE|TGJa1WJ)NZe@ zg0EzIV!sVnL>vu&96V0YtY zjHjGhI8%$>mu#r1yozrw;FIdP)$6v3dAMQo7977d*yt6q35hNf>Ws>tck~#$Oc(f$0 zArxA3$8p_YF-1F*-Qa z6K2MrLLre)Cw=LB)D`o#<61EC02;}5un*W?@--hnT?RoT3&s_GqZ{Q(y9i+~y*x0? z@}x(o=~W}_rRT$^|33_)R%rZz8myL^f|`tc$6BoK)wx#O|oa zmPU9PIbUSa?Oef)6Q}g`1i2HH4tUGknbN z!@^2>1%z0amlGm=DL=SMgOkbT8;uFHg!7Z4UGc#eXz6ci#sq|=TX^9flMV1vTU4qJ|{!YPc%uUkCQ*gS17ZZZ+0tJ_;_%#Y{ zRdA<*cPaQq1;3-<4+%l%B?Vtm@oy=Zg&TzRe?q|v6vt56;WFSryx8M#~*o`5;`G^D1qiS%Ej-YPf0oNC9c8^YJ0X*QQ zp4ZZ47(1aO*z6i*jnL(f#=Uw|N2ostoy&}}Tio`Qx4Wowa8}(<+<=kYJsY%hpl79Z zsi=Q;A9*rvPw4E0j9uQRrsnLT@VzdSxNdF`eePRlN80$Dym03!U5I@zkqpb&vOLbxr8j+Qs`{ z4~DH6^z2-#__o)rQ2(whj8Sd~<5c!^cTNN1(Kk+UtLU$LLEpqULO0L78#E2*X|gT8 zaFa{Dd4_RUp${6}x!^4ngD$<&n{IOUg$_9j`a<6i`Y_qu^FY@Jh$|%0|b<61AJrQvh`aGk!@$~@s z;kc%>M&qA>9;wk5?Z*E8t)Q4IJHa3Hn?sFmJI5b$gJ&$-Q5WZquWTpUaNtdr=bXuC zYj(5Rj=DF3PN3}5ZdTokm`B;&fwmi6KgJ^+x_3U*vlD%if$@UA9_B30G+>ObMO-L$ zz-?=%&iL}~y*TrL^A3G8+GX3R{|Y^J@RuCRmw=~(F3P^ede#H~s_0`mzFHTLbGe>* z&}PgpcP;avPM?$7MY`n8C`;1Cy!PJ@S}a$Kycw7WAF8ww0tOxJac9U}ex`nux%{r;434PhVsT*4;j2;}IUZOhJ;4>D- zu`LW{u)lflB0bAt|MiESFX!x1#swK?A`V+VWM6`QKj4QBelTQ6J#^d;gAJ?&95>D} zai6n&&Q5U;v`@j?sWDY5H3rVa;+NJ_sWI47J2Jhej$?+_aXdroIA-?LaqQ#KKuq^)AmaF_SI4n$ zQf-lHh5eLTp>itSf2dh3bQkgX@f*b$x&i4XHy+T1EEaC3vlFU^qLwg5F(+9AG$E7p zF}_LaIQQq_5NeBBLy}&use`FhB2S?O*^@?6&k`Du7i4ISNKJYKNK?a9kv4{)A()xC zd{|uNn-yUMKU1gy@_RD$BJmajg~svx%U!=O)%O^~e&6i?0l$u12x8EboGP=d1x7u| zE12~Tne-@M zA@>mW7g)jh-$O8MEa?6Y0q8ac#{ybY7z6dVU@Nhy67vPwPHURP0>MV6O=lg@=?X4n z+Kd{3g@V5#@tNe^f^%%Yk8k$&Gej>w7a9FN5=mc%Tz^3%bsK}?MMCQ~27g0xTDLJs z6(?(vtSS(!CZ=^8gRc?Ox{X1$FrnLshcmECM)2>boFG+S61t7Sfh?zW8-tfHP3txW z#U@JYHU_coBy}5uR18h%HU_^&tXkGk8l>iqwMt@>g5p)DMq-nLXOc>-#L9vt%(<2w z0NuvmCkWSFfxkkxF@quIJYyFA3^y{a0lq7V+h1k=^;1AGoi5EW5ruAJrqpd@ZVuiE z!h~*Pu!aoOk*qyxj}`nDQ|liV=w3^XSVFfk6B3@JZex%igRQGLxYTV7rV+hbqIdJY z2fCdjO5MibE)w1$>$%?w77|V9HU@>c<%Diya5u{)bQ^=)i6(R#gWTa+jcg)y8}ZBo z+9WAIYzLoYYO_R-*g-m4uy%==Hmcpul)8<44HUYK8T7@=pXyV;kJHKT<389PO~QLx zK%md#c<7klMaaC`WRrLuKr!>O&<}@Ok`5^ht5e$_(G&93I@4WWT|R!31N5@ZHut8zoFE$n0#68a(g{UJ6XnjiVTPI!$@KhTb4lKjX$y?uzb3&Km>*h&EI@+yvzHXn8ItIt+@G=FOj&SA?)4sQ zsZf=gC2Lp+IZPUvT5X!7!>?$XR*hnC1be1d=AVZTQlMoClL!A#C_v#&@ieucFzo0W z+;zV?h@-SuO}j~PEcQn3c=FBOs5rDF>D z|1oC*^mUr-4UgDa<>MmurlQFBnbdjNR$hD)cqxg@S!2%$gze$0?fK<)cDen1qul=H z4Xf>+8G)q-or?;{V9`Z0iz;V9E5_cqdd{QwlljQnCMW^fTcEH6y{mG2d-;E(^u0eS zDJX~(<9*u1Wc*Ma8BY5T)RCE$Ig8&mc`*;aTZW0?;;fDV_R^WD%nZxAkR)b3pCWVc z6WwkM$K!&oz>{CNXXAFk_vdY^pf&9YFTG&J&~WmtuM-qSgsMT^$EPWh)YwP}`neD5r3ung<0Dx{WQ8H_+M2Ks$ZIVR zso!yeW(U8LoKltJZ_wbNq@7wiNlgyKNFhXGcurfl<73cJ5O+0oO~}IsD1~^Y3G_2I z)^6Imf;t!{DtK(t`anX@gE})S`1qhU&wr*Zgvkr$R@t7^CG2ty4a zT;ezrt{h)jJjR$pxyDNRUWdv)f_@~Qy>R;6*+z;LFpZ-69UG~WRYc3;0VGd6s_94k#oo>`gnCMyz7!)qI&CEJP+9t*GcZExK=Y? z0Kd@+*GpI!tD*N|US8wyWBya+dg*Niy)wj+9^V`^y?TVb^bUYtCF1m@(>5c*OYg9UKRzclJ-%4zGNkbf&e6ZV1811y%!@Xdvh}0@&NTkMrXYa0 z#^dgtq!B<(?_q>dyiO;rN4{Yi@#N#K+Ea~jBl11_0r^PRGwjCO*@n=Ig@{<{s7s#ycxGq){9IFXEWwmf;+LQBPq zLtyQ>Y@@&Mo6IQgaj2t!Zc1FF7EWNYU%5``i|P0#ow$hOyLDopM|v}X>wO+^yjdjn zbi|W_8eyqB`CL-^2QotGH`HxYTEJps=h^akp5wiqOGi*2ZF?V6a4D*!+&&NOrTo2; z5b_-Q!X>^O6Nva$1zQOb&$YSakv)c?o)W1 zg5rEO;$KiW52%s;pB3a=F!3A(&r(qMOh)_+g)dZam4aIo+^Jxzg5OuLQ^D5=QP*1v z@?b0L`>TTKm`|d91&bBDK*6~RUaH_mLeLY=!T?){V-kK(rPFyF>BSY~UV%9EV+gYq z9I0TTf;?KrbRowFTtXc5FIBKwrPnFASHY-)4-q1Nn}Xj~@!wJK#|plq;2#N*|1S#K zxS?1-j}tN;w_Fc9cas%fMu_w|3K#ba@H&NWRq5jX#2*bqDqhHeS8v<2N(q8bWfyOJ zX{jT`z~NKNfy1e&1dmeQ<2ZF8vMe}ccN_QxA%SuMc#S< zxewsC2S4sP*p?dnmg2{DW1L*aPPs3(B#I;`d@}pEVkARU=|_DZ;8*rskW4M_{MWWo_jHqM&pk!)hYo9 z_^mR6RErBWoDa)1YK0m0OU&)d;h*LC_-l;-4HV?sY6kodBFdbOKPe2Qa78y#eP734 zNI|$SOY4V>RK7I7B=R97dA=;jZISs!L7_5ToH>1f<;{5+)Cx4e%#vp18GmHlgDM4Q z@g8Dz=5kb%gAAG10qax6sELR8*N?>rS0mCaLr6Y89?yOMqGudG+J}U7n^tYuR#Pjy z0m@y9SVqKG!jqkh6dtc|7!r$c;xpklOtZi#E|_Bv4J^e`ymjKj_d)xLBG^^nhcztc zJirk6`#7fVy;+GQ@eU49GOWaOJ>eZWD=D#N=zaVz@Mb6F&@CcnG8%I zxau>ui97(_z_jrlw?Z4d`dV)XkIc39U98sok zI{iuP13m+(jGNZT{{d2x?d^}$@#L|W-$Y(PzJ})`1^`$3l?{6Am z+z4gJ#CKUt`oy~|_twPaDh6qWN&^s=rpGnf`)*tXdI7|d9?LQx@rZ(&-aEj(7wh6h zzIB*5^R+XeE}b?LxDGR^4G0BF`qIIvi16~a7W^GY9QotcLi4v6VO<76BZkFbmnT98 z=OfKauMC?I-Co9PdaDuEWe_yhA|cFxx|okwHGW=tvp|pgD)N_(pQd*;!d`lNkifpz zmrkokgqPk@4}SBXN;<2#U-UX_QxK1kF2-GwmAIQa4EK*IT&2yh1Z z`@Dhx;!4Maoum;!UF53>W3JTcq-n@^I^Q%_BH!b>kcS@WVn`F$!`M}nA#xyM@aier z!G333^&Cae_#*~^`we|*{xq(kH~w$`rs3CQ1hf-(v5dCGOigC7!6nBsc_qI}@Sf+n z61FROo#;!9>C+P#yxg<>oO#L1KA|>_eDXS-Zy@@c2j@#)_Hgp00Ussg$<f~nkoP6&U9aGc3brcvn1as|g8%0g{IQDvvx3wdARS>f0~2>8 zapccpIwnPqDks{DKN_Nl$S1me!{#+5r}?f{GEtc@KaF>_H#oSsl{h9Gu>t(rMg2DF z-iD!P2c0^+bB$+%y`x{^$Mo&pD~S8|?3$kEw_Q@wQTZ>*@O_p@IcvGX2(r zHzev2h=7M#h#L!2~wZhj=ufbdS!C;Tj?b zi+4){ek))kWlaeJAG|l$qsjE--aN5)P3)}`8|;&8!2kY@w(5or>o!7oo~TZ-|Mzac z|9`mi{-;V6Kb{>ol#(#5Fl`f0Yp#3VDdz*uYXpAGr}q`h8R@y0#_KHn0{EROAD@A` z41(f32=|Bj(rM=-LWh~gX9@2ded)Aw5aHe9(HCDE;_!Ge{P?jQr1DVBf?AXk3d+S^t^j+J}Y$@1dS@(++haPrTcFga4$WIZ1_AQJ;rPPHY4n%*8qBa zp6g4etw)5HUI_Fwf86lVJwbrPFQz?xmOG;g9J! z7;%n#@;ddNmpzfZxChoT`G`)+-PF=Awzl$VCH7>vvG}|@shkUmoV%1mj8t%pf{ub` zD>y^JN(GlF$oq@sYZcs}V7-FZDA=grUP5$xtAbxs@sBH<^Wb#-cio zr<)q#OQ?)9X6jU_+;y7VUH^Q6|&rbmst`0XPdV18};42i-E`W_PL6 z;a(E%aF>NT+)LX#+~u(j_p-7M_wvdPcSTi)TUFQLu59RVtD_yDchIdte%dmx1)K%A zsvRW{b-G!Vo$ipT zPIqu!r#q;j)6I!?y2D#L-7=IL>U6r30doN-0geJJ1IaIXk;xa*A$cSC!JyD`?`ZYt|=H&=GJbyXeim31BNmWB?uKHA}KZS8QkJ00#w z&?mozfFr`4ZaCEGo@I2pXSR2`qhp)cw9_qW?Q|WK zD{wmTyTu(3m=9P2ml#b)~XKowz>}YKtqRn zd$hy7qqW1m%i(x+x}O1DT6Rm90eM?SG0v$OO|c6@$8d(JDK?5l!|^MTHkh&(&VczhEJPU(a5fR$0Zz3gTl4-aBZdTY3`sM{)Q?!q(bIe*`q zW0OYU+!gL``0xS0I2T$8Tl=8(9%OQdVW-|iTX+tV^+7wK``7CHmMvs-@cjVDw2MK5 z>x0OLH3eSv+EF^&J0Zo?#q`a0+<& z#b0Hecp71su%tTz_;9g2XNY#e2Z-@3>OSKMC*qx0w^7&N!Z(ZX?|^oRKFUI!CfbH^ zO`L~4Znn6C(FeIW56k$h1n(HYXKp+4;+$o_$?(Zg4Bt$} z4X@KTnF%>C^Vt|{tm%*V;Il){1KD=%Pe+VrSf6dY)rBvrUy3;LUDn{z$H6-@q)#9_ zWTFq|b-&0n$94E_QH44iqVYeQM)2z4m@7uIEk+rhC!wf$1$ibdjBggsK8EHsxJHzD zji8x6pc{H0V*CuuDSQX%t_E%*o%u1|!4&Y8YDiyIaId8n1+33>jY;_v1NR zig=@Je%F5x(uW%O%Lv_x`vyLWj3_^o;EXQjc8>0|CEd<_&1#HrzL!3FaDEeGX5+h2 z|5EtqVL#GWPoAO6i2FbOS=8~+#CzR`&b$|EQG@#c!hvvudq40n@F?&M;CBNL0lyP? z7I3UH-MJX!9K;#ncAVeky%vqX$n&N%q`xKd{tkE#IpiJZx{I3!ybWBhM_ITZ<$O6H zp3fEGS3&EjTkgDyd*-M+8@L6W=SycH&PLqy&@JwT2>TG8iF7~GX8=zD9>96j=?JGH zT!ypI(-00IJQexUkZ%g`AaI_4EkigR;mOE%0m2ywpO1WJAm4ewGl7SYZ!*Gt5T1m5 z=OP?J_#EWxi+rB|-Vb;d@=ZdxKf9!8$d`+71Yw>{AA+!h@L=RCM!rG7OMsUm zUk<|K5#~AfY=lb@9*BGskZ%C+iNGfzUlzh=BOF4${s>P(xF7O;0{Qv^KL_|^>P`bL*57ul zfyOOeoWsLo(eCNuj56&f9CN3KpL8t;&v8DZfzJf)1C0xTAD;Y5{PEI`_{G4F!k^7W zsKbn1-u0dE^O)Z|yDU7@fBF9V%S>Zi_anGRru13FGx&q`UfBSzI2#U=V`BXt?8EtM z`CM7hoeF<6KJJ^b?q@uCAhCBYLmu0xjJJT^#aM4>qv2YF^?kPk&n>P$t!n)VBJbD! zDxZmZO?~jM?+^Ywp3}`G_u}4!f6NihZYI`sU53y7uOLqpdn4ZCW;AM}Oz;7ek@q(0 z<+=gqBMtg!*l%flV;55D!_AMGHB)?pM!SifCHs7%9)l|GQ6%4DOfXN3ss{H~ zq?4z7gMB1<;j>QEKM%GSP$u@jd|p-3&P5#0u)Zy5Zy3DV#mzC=SD21D5dyw*L}QHY zXPf7bipDSM)7px4KuOe)5dE-dxn-?xsoFo{c{~{70ohA5@h&XJ3(rK+zbJz}p?F50^B1{K zb>L%&d94QYIriY#BR-M?eoSWy?v(}I$a7bg^E1px+G}|l zwlu!ydTqqeZUuBSf~*7k;+s;?9`Y-E<8lpZOTLd7Rrn5pcNq)&z9NiA80(y`ZiLJH zdYteLS@>?u4IdQo+~?{v?o-1^XIt4%Y^x3apT=0FqF(xB_0nQ~yhkPS!*3|;U3?1f zZ|GONyLo;0V)_QzSC23c_||QpjTl`&`oWJruph&`VW055iGA{)D3gPHqMxCsiSJ}@ zIhg-{#HzR_gnI)}(_{Z=dgwRU`yicU-+mFaynU$icHuom(-3xRz@tw475IU_vA5_u zcvlGH=7totT}bL$Se4S-lz#C+rU zkq`ETey5}z5P2Ujz7mbJd@>$mx_Z{yW;cK|t3`P6n zUb6MQJrVQ@u&xO{DjP(9>NLtu8PBzq>rw&s%6K0{8w=lLUE@q7G`m-zew2mHp<{9KNk5Q?8%!FRWz8|vDQLa@{`+HTYib$p*}N$&ZhriB zcc0TB<`(b!%y3lro@_+lw}oyK{m(k}J7d|pEm!k0tME&&Xc`VMl$OGqa zTN(HXFYKZpP0SM$`z!c6ZSjLYw7asqo$EE~eH<`zlJx7nD~9@_^gaAC=KBJ!MR&D@ z@4>uS+>P|(w z=|fWALu?1X*YLSxA&>A;sou}n|DVM?I#LT>3ZGbBkrWTE3Zrx>B6RVkg`(p}=XGzQ_Sf};{+jyURZdg;2>!VSJelub=`XG2* z*q@T$4_Gc3u8Cn^g!N^&*t7Bb3FD7n+2UqJTZ9kX5A?gJbuW6`8d8cF!qyN(pIQ+; zRVs`Jlo6qH=_oO+r-`AUwkedZZ3<;DzxJ|thSxu1CY;?RQu^e>WKS++X`!Ari25cq z3h3U#&fof=B6wJ& z*`i!&wg}dQq}ifj-b%yxbTvFl&@jSEEd}HtA7)l%Ovt0jGEx;#mXb#GR9(sIsn{|) znQ9nk1{Lr8B(w(45X`Xc@Rxha9q!I+IAK8fV z+w+h)W%Lvjux>>HY#T|f8+!n-lykA>8rFRLf!56-P=eOYNR)uq%^y(S=T~WdpM40? z_DjrfpNFEp%SF+jqhffXycI=#8#f~{#rGAG97C=G_AXN1&D!lwX897n*BbU-mj5bq z+b=P;O=O_CQQy}^@H%FAM%48_EBHQRQ+%l=1mGkO_MnjhY;x-0wE4)9I^+pNTceD$ z`;n5Gdstvr+A)?GiWLWGzO-Kx8}@sF1=6lSC8@(-5?C+|9|w)pa7`i&VzRwA{4mOe>IS;k4Q0vrwkxrCr3d zBAJ$-_BEzOBvz322~u$+=A`kcacVJJY+()uvk_oY`c?P?r!bR&2dwkbwj-N$?sb9@ zBh622@(lv>8E4Q(r*$5SW>({mY0c^L8KfC~@GZbF`}`JhmSH~uF045hlHeckhxS+V ztkr3|nYxDM*ulb((%Lgff0Qo-?-Yi$ZkoUXp*8RpVqLLGU>PACg@g&E+XNO0)z@BW zSQ{P|SXSCja<)-ejzzbPMF(4(B=5PQ)is8-`Av~AZ1hbLRcBiMH>{z!_+QjF6@W!BluO6EOTMJ{hGTbYS% z>Yd3;@6*i0rg}0dE@>^PFPoZ70+&=+z>pbM9UG9b7~_y*wWigumu_T9j>9x!H%ZKw zwi6kxn!0v9vyGYkpXlQ-SD!(kWcJy|%&wZux(_Ogam5}TCvU1Bv7o1FF_ z%hgJ(3|mEDYuN!XgqF5}*t#{qgdwybL(D1Gli`rc0H2t|ZF-CFuctGltOrx25r$Vx z|Hcpvq4_s4HwW)>4$x-OvEk%3?E+R(C(%8Yn1TL!&Om$AUQ3SHR+-vrg;v)Y{%sOF zV5LENY51?=;L;FU+9O1-mgwD9+F-VGJ4e)pA+)qO(H*kv{Z`s>a&e7BAF$H0nEFYH zK4hhRmwkJkMBA*i7dZ)TkmzA6jV|^4jcg(fp{13w*-bL_VLL6xvdt1bVy88;qFrL9 zjcT_;G=%2g{iG;+97p0GMTkGurzt*8rxYI#w%U)do;{7|w*kQbl;BX0Im#6GKQbB9 z@iQ+QSW^v4S0hd4Fe|e&P`jCD4j{Gwgy3M}w~!a!i-Fbjq4{Vy7Wpg1crb5ZuEnN?wx zgTIaP2axS;{Dyo7$dDi6PcATHD6K!^SRZ*K;bCt8ZzTWE*`_(XiOv0k?nC;85SJ;rd(Q~>nUy}W>nEX8`nhXAZfRY+pVhq>|8bxIJOSZ_t z@(aO@rtw8vbjYF{&Rg>VQM0*_6u&IP^RtB$%?E9cnR&US{}q+b7;zbPWaj;>a}$Fk z%`cPYWh1Atsq1;en@dJ=X)-skS>~dVZAoPD$Wd(I##JPo6LCmn3q9)DJlFBINfX-V}6CGI!3{y zDx!`e$^d~@M)9r1-rtU>eu!cp4lP2IwSTfPCJ!;k7*i%{|orKG7}+sZg4<<$WkEV zYVgOb%%!>0p>+%|8hWjW8oGyoYHle$xtgp)!BpOG1&<*V=Da-Hl=s_kF)d34GF;5^ z33wgFZ7inpM3V&y#cZ8qGHW5XB<3gN!j|GA(P89`&a}Ct`lBV#Xw5exqW}O~kyTV&*1deyd_uBx2rGF;^yH zey3uZ5;5fk7 z`q#^_SVCktyrIM}Z;-<};AK#Inbh#bU=|=3L5gDZ6Re*pma2vF36>>iQf2mooEif} z33HJ#AdC{XaRR(9sgOd0hl;D7gE?Y7eNAjv&3TgM87Wc|9J33Gtg0HvuLSXIB8Ymn9Yc!Ny!A{<0aYJJ+$>s~bd6mGd z{khrW87ooq5@8keYKv^(Nd-o?izl_YXpnFwuCT>}gi~pSEzRb8pw4;ep!Imb@37c% ztTWjUvVqrFoET>1nNJb?zJ;zk8cKT9FkaGJE^8>2 z^{E;r$Ql;N8qQACa1&~n$+|C-HIO<5hPsAjJ!-gA)-X?Uy9IXNBssKg!w{iD~dBISnf1 z(Emve;bOyiob%Do{+uiKJ>-fXiu5F%qxL=7H5}H9#1dg%QizAV#*Bo#2F~Z31fD&sQOYu|1H=E216&`$ zb@fL(`Q$b0@b^*B&bY;4vLbPgtfyz}gwJery0#2~LFO?>*oZ*SCd^2n%j>tU+=^JF z!>D2HmQ6ATUT07k+mFv%=D3o=;>bl~$5+Ey@^Q0#9_+MEu!j`cYv|fKVn1QuGqU#O{w$?g^Y{e|<+_@|?MkwfxS0&M2y| z-@ksfofnvlzG$cK>#NJ{r_9ERxfN)A1zc1+g%uS~jIl=)HM0xgsr(vvrXJ^%fG?O5 zE}v*$R&M8)?}SV12pV~D#hePesu7;ki(oh;QbFUrup9jN_4nAX!NoTE?Ate#!zp=W z_m~NR(Xc3T{|)F&dvdv5xLS2;NnmNo*o!f5Dk>uO^XBSt#pCBj)<>$}D~P~mFfu=A zm(Lwn+_WY#$NtH6<@OoncD*o8EIN~23UkJA*`85e42R(3*NiP`YQ716%}YyY#JIG` z&T0f3mlfF;6xk188o*d_U{poL9yl`}58v8$H3kA9_&~Qy1L4PK(qeI;J)+zm_-;D_ z^{efx0uMz#S4*?WCyw@q5?aYnPtTC!?xwmj!NpS@JTfmlxS*HdcJLq7b)4MWHj>8HbUWFvbz?%_DGP4`1XM zd)#$P9s&Qg?80Mk(hl>@#bd^!?#9}?Gegl1(1_L44CjgJ}c>eoNwWL z`JA4aOaJA2?A^Xy{+5*H)XGf0gC%T9a+5mQOJh!ENVkDo&+SOlZP4)zh%fWR=ON6i z9q}(>`8%19am_ckP7TNpzTq)lx=z=$x#{8NdAjO19nY-_KcwjRFv7gJ-Qdd{+BqjP zzh_g!B8S|LghfA|=wp2f@`x4N=nqQ#RI)5E~ewc>XGKjvZA>x*Hg9mekne$3N`pS~Do z+AH||0zc+Cj-S35W*W_XW#Gp=RIyB42s4e&AxrUNo_zfD#W2$r;z!4k%u|VpHAYn4Xg@hpt$qht6>kuYo5(5MT1Vcth2}vYD zpwA&DOj>9`l&W=zt@c@K`=gXvtF*| zs(pR^u;FIywTHFmefDtH_uyv??ex0~zx(iG7#<`ULp%MR!S5yf80Ja*jG>)=AL93C z{21l~{EVU9`0;ef7<1Gy34P~=K>ztb7N%jWekbAg5%>_OG4c&_f|Scg#}%{#Z>bCz zHywD&y^65TgsCpQV0wwAKkzNzc2{xa3+-`w@#-u0=F{uuL+OlaO`B3mSyto5x(chN zt{yN~378!SF{Nbw^yzWKat#2g6_&U6UHap<*A~!zOf48YXHB+ z>?^SEn58k+2?ZX+180l|Yx2~RnHSB~1ub4!qSfq`u6)0N;^ssYLW}B@w-9{`9GH zFG2@i*SM;5O;uBws#5R6_p2(apxIx)QJ`fi(R-@vHB(kmQPqf#*#mgJk-1WXr@*s~ z^K9ciTcK53hLF|xyg{XI+4E=4Ur=HxBy^Szh-Ygn8Z5LWGOI=xI^Epq7h8dTf!znb z`|Bh1*VeDs$nNY)#LHLlrcIwSy<~a-p@Ob6=bnF&rShrZ^64~V+I$~7;^g*K0q{5M zrr1YV;0o2Qu%PBOI6bQpko&cjW*n&Q#0&A@qqU-}VpWxD9Lxaax~kQ*PzH(hb?74N z+2@F60^#Mgt5w(;C6lLM7AaX$JROOxtf{aTOuyj5>2pg0A7D6g*ix2$daJruqX8A~ z4QR%f#_KI|qAtk!>w!MBvL zPcg>$&4oiy-lP&;F3My1j67b*FwQvqXcohQ)v;$7j{cVkH6E1+chDpTM~0(WgI_S+ zBBa|6H>S&#lu5S*_CRzCugF=`;bx5Sy9y3LdC`S_Ik*Bd^7g=P;!s8nAei$enE>E6OJ^HZR^9yxUEB_vHK6jkM+m$vrdBY za&V5l-w1(UzzWVcLHFb1galJydQ9CnN8*B*(XYd}=(+5>G}G`Sb{_NYn_zAzzq`Vn zVOX9CFg)wB$xGI@hJZKc07f2pZER~4Ean`*#J>l7dgFfvEM|{S|03{aFHL?9c(dmw zkM5>!a+*QOUkYmW*yLA&H|GN6Sw70w##ZCdoCDCGdEXoeK>mvf{H_H4)&%}v6ZrcQ_rH{)ZFz|4iV2m%#rqfk%>_p?tnpY7BY4^=l0ID0p-J!~Bj(@Sl*tPfy?% zCGg7=_^JfHK7n7C!1HYyW0=34;Ca!*d@_G`B>3N#z^m1iEMH6UvSm(La8E!mNwX6O zYWORX5hOU@{tZK9xJVC@l-fM zrKL4`V-8VqP}RXDy8p3{QtUHv0#;tLvY`yGdkL#HIcmd*ZGs*q*n z^=rBAM@)R;t!drbI$>}#tQLz2yUuWGSxH&WNsUtVR!(9T(SI6R5zIxCyn3ZOZ7 zUSVF$8iw&i3jY;CIiK=n^zGz_Bl zcG3|zE)$-gt6{ulq+^k9QiR(ebd%7Vgz~xL;DK`=se>asq5FkCAoPIHgF+t@`XnjZ z=|fQFV;UAbY_D2Ur27%+1gr-z(6g|Pd6IMz%1esZA;NeZ(0!87O475S4@e57N*7J7-$a-lUs8-%VG+ANgMOv>LQ^fsa25&C_h2ZjDj=qo}yguXBI z_d@yXp-y!$SW}=$k?TmZIeR zL1@&`JYS}ye7>4Sx=83Up%p^aC#K=g_X!zpn^5(w8}M%kk6R_>9}zl8=oq1A37sKy zqfpLglzY992%-DAYm6r+Y-`AfaP~o+WgK&<#Sj z3cX$Ew}l=M`ZJ;L3jMv%WX#7*FH>la(D6d22%RT%h0uDTR|(xA^hu$=5K8a{&PVSE z{j<17G5<5&F{JP(b|UFSai1=9fzYdj?j%LH8-#wH6btYJ;{Sxu7li(Y&_9qO-d}`< zx`9XfgM=0cJ%<$GCJQCDC&Mig+932Qq1Ow&l@#&Xg#N3z^9qOY9~Sy^p}!RRp3vV5 z?Sp3n!$*aV6?&G?=|blVT|$agk)pe_QAe#GO|$^#6CEzZIH< z=Q7><3C$JyIiVK{Efsp1&{m;03jJ51KNR{ip>GQPjnI!tQ63l1chXFu1B8wd%Jl*L zCzB$7jijizYlQ9)db7~Gg??A)lR{q*`j*f?2u;QMfbvcknlE&+&;>%PgsvBQtCP9rLg-3T$XPFZtMFTe-Yf182z^56YeG9o5&zdh)3^>o`S{6n(vdyM_$!2O7QRh* zs!C8E9&;9-sfMB(D0j7&P8vThN^|^gu=OuQB3NQ;qA^!L04~Jd2H+4t zcYZ4{ctQtvDVzc~R@<&H0%ibHVTH!MAl^ihwW-|#egJjPLs4Q2Zau_sp-6dq_6Ep^ z0SBOeTgUf{T04dyt`4v96irwa4zfbA^7f=iOM5bKjlN&h(y_PU@s9mP4|nW~J*@C> zJ`n5*V)#Xhc6GM2OJ1VHKLD1&L1MiuIIM6R+uAy;X5b4zhkg>eAF&WZF@%fG?{y&h zoV~C8#SS|*=a3VvYj;LMA0ImWLl?JoU|HXNdu&tte&B~VkxlKkwYfb#TCQ~Lo#+4xi{W>$sMr2jQ7>Zm;Tv`xz}b17 zdE6d_eB^g4%8c-^w{(P}EvgKYfGI^c;BX#ttP6lYIr-4O*p!Y*z+u>HJqBE+*HOo< zz@d4%6HjrJ)yKDE8~jml5yHeZL>p#Ywi9>-z_YQ|AI2)Vk6na1v)Vew>;(2z^NpQ& zoDWQn%|IFtE8L3x%@23%r5*SS3>T*S3k-hVg2O{lKW8h<17PZ`Xl>6&{7B@+&J=|0 zi*NNY9`j)XedkJhh7tqdYdcKi<_Y?pO>tmbHZP2kwo%)Im%P zOcMivThW;uoqA}0?6Ho$J0I)V7kx~n@e7pmYiFQuA-&$fcRCw+X#PBS^dI7y*lm*! zeFJ>*itDUbfSUc~VN zmP`jQW03D9hp~0+&U{%1+YV2i$3DHRjZ z_1HVM^$6MxZZS|`t69KZV>}l;aF`xsy%GCjFEDuSk3QRx0{>)SvRS};vw`)-Im=fda6Ev{GtyKTQCWIfcZ3i;2twxT#lDLo!e%i{vyroz@Y1}nO&cr|EuJR_EMpdWW^#yre2*zLe6Y(W2ccuOaq zzuotbd7&fJzN6E^PsPJrHR-ZFR9P9wc2k%&@+_JJd|nj>X%YLV0r^P-_Ktd1;<@tm zbM^uorWy6PJF{g}TgTV%<5@LR7rQ?yO6-WnT!$m%U zEz>^f;oY4rL)toW+BZLL1Je#CfrrzJoOV2qx<7Uv)7jL{aq14a^_^B{TDy&L*DLx+ zM-uYu4%z-rGUPCy4U%u*^ZkZ(p>T9Ct_m?HMxwhqS%<{&<9vX2!dTqV`2f-g0pBYF z7|;6I^GIiO=bp}P-^KhI`y%k7fw_nAmV^G!=aq}+-GV7?9rJ+4lLoie0+a{O^e}LR z+%~&Cgu3nzOeJ6vW4!Oim|1xETS$Zc2%m}Yy}Gvp{|NPy0ypHd57rvJrXfr^+74~> zv{P^+#%1GS;_@s zX{d-esTUuf7QF^|XV!!E&T9`1Mz|KpH)ShA+3e`#Lq*Y6lzB_%v`A~GjkXS!t2^4R z6?wDUt~>O!YK!ZnEp9|x%#yZfK|5@I=i2GO@WJ{*^^Y+60@g~|Gxm1iIozG|XfoPM zk0*_979#!_`XAdWY_)b`8;0j3(htlF%u7zhuV?q5-Tq8I`r6p-?T-uvF6hB$4x_!_ zBVL|vzk_Jk+pwN^p`#hk%l)zEJ08F@e=pV~tud@au=cT#ZV3I%0nX9=QN146hqc&# z*!NnG;#tww@g`*2QH&|{H7sG@BR1t=U~|EK9q#`+TB2nhvMe=66he-tg#<_8hJw4_3 zw1aDKtnIGb7g>aGz`nB3=h4s2yo&bin1#L{Dc*_x_%i&850T#=c^U06?=a@vTUukt z1DWBtkiavl>SsF2?8LUI_1@mt<7m5`o%@QO=-3~50%iPa=U$}A zy6qmTXuk$^9m4$1J;dH9+7NxJC5nB`&PO_4LcTaxq@q6f45)(A1FJhjX-|+_K3@5g_eJ}St$TR2C zF!t2Rc)qH=V&oCb*Dqnd#$K}d(GGsgf@KXyA5mqEAwL_gVYU*6i=9Uv0zu6)nTM32Va0HELhXy2(M^ z@5A`L2KjVj+b|ETfIputEhzKD9bDV7eIhH+2G%Q(SESR}(AE)Y-q9IrM*X3EQ_$z5 zd-1Ii^Z|P>{Mv5n?7w0M%C@7kNwovxPE~OotW!3^Z7b3Q#$NlrB0RS+=cmPP&fJA@ z%>3GzBeEBRHYnUjwBswPU6&%B6KOj<9P`|M!A|TBx4n_qJ4#TObI?B5M6Nqx4(X0L z1o-&ZV;;UGjDFycOU&cVZ5=mr{%G6TNeq32?U;e~|0e1*BlfC{|0gl7^ce~3foC5q z^CXr7@{(gdc4-0J?mxIoyP<4J#lU84-`<|i{&%plJxP^&Q#q>dosPf{wIBFdJtdnieGJYBOxETkzo>|6m)eAV0y)bsvzQBxK-CzB&Yx?s= zkiqYtq+#q5OR^AhCbjQ4#C(NO56qiYyyMqqPMC->cJ^MTb;)6zZ*`y4jJ}6Cko&X# z=vz3q=w5^}6Hk+v@$6@LEDtcA(SKAQWuIGvbfe5O&cBGaeShS7jJqc=CSOt5upF1J zg>h*W9ZHU(4_X|T@I#zdjOYCaAMGHPFY_0Q`Eyp5h3^3&y$rO`f#_@KxAP81i;#~M z!1hJ@#FKa6&v+~o-LfE)eJ0}|zBzz=@j1YCosG0pSU=Wno!l#_7f==}V|^+ZkU=oV za6I&5w;;A-cMTSq>Z3QNY4pY(7-m+wpf?JJWKUrQ%rOGcjy^<9>}4<{d&gr!_VM5g zQ%9tkuYzVIS;4P@X8PcXcumu1_$EWY0Hh%#_+J(^h>(KENJK~iL<&1F6;;^Vh7x%o zejFrX7V;24lssLsB#4rOBp1RC7U{yq<3$?u#-SQ>GTR4n%<*w4hiP2O;RcX$gjX*r zSJXio>E{S<8u%#!vc>zfDl$O$#?jb_B_czb${ibDV&j|_1fI;-z>~4=0GZ@F0tE)5 z60ZsS6=7N9gSliL3;k-wErY?RoRDYhx?}?}nDtAH5)1^G5X3`Q4qb%id2se*_&BY& z8V6d`ZrFhY^?9t8053Th-l1WW5zzSxug*Pq$^PI%8eWpVfS25hh=7;;2psSmuR#1z zlB4|qFIfpe8eY=*6}?iDLtmlP2jFgp-7>f(D}cU~pHqNyPB@o2Fo1{Q-!h9H;9)ln zc=$~OO%DA#{&r`q)IZ>lf(A*WE$wRjf&a(&+ao#jHi%1D!K8Cp!|7}BXB&P%sta$0 zU(#f{IN4TsF1ab13x%I1H&t`t@YyK0hg28lYY$1&@sLEk2tJ+*?tE1&g;bXedG1Rn zM$$#CuqKBnr03TV2RAZkhot%8pVJrGVmjdk6kw3*!VK*p)rHqE;YB)RTKE=*yjXLQ z@crZrQeAigLmH&I@Q<0rr8;C*_+^GPNOj>%rf!hx!lRg;L8=QEkt@~l#)JuH=^@pH z^O!=p_A3mJWC|6Un-qSWoI$D!Ph&iTR2N3#R?;d~0Fdg!Rpe@jN~)0RBD7ITugmdg zuoZa(`~;tOn<#5F-+bx$Kd|D_V-cyYH&v;U>KK}hw~~3QryL^Hh0lV;N2&{Nri?}l zGKf^CTg*eM>kXALAE}O-g+Quf;}WSZyq)ZN&ED#S_pyF9u#H?G)rDU~@;*{sxF>zT zpmTAL6TXs}@{sDn8|mvI)rE<`=p)sIzf87;MI=&PxI5WaE$RU`+`_mXQeF7Vl)Odt zwCsa!Z;e!!21hp=NOh59*!Yt=HAr=YAp|@FqHcQ@8BZZpTO?(97{>ut$`TfeRoOyr zspdlA$H-lxxo~(gVx%l%^8m5Yayb%HmY)vCK0n2udm+KD!XLbuho8MX14thgtUG&Q zhCz6$V0GbJbsUHjD~(F^&vwisFU?RmI-6OMwoYm*lNvCKzFSpd^O^2872@InE-J>pR=F=6 z$Vrn^SYLow-J}A{?+g7l%f49^YF^*U#wN?YMKRdtB!_xK^l3Y>9kp%P>>e6PhI#yu z*V_9_p-Sp_34s2TjqS-G7ELfT%cQKv2K|eCSR&8gj*VATg*~kx+S#T zF3~L^+$DoQ$HL6kB8rE+fht*yJhA#0Xh*6363vwiZSd-HiC_Igeus!VJ)h-L%js4Q zi#ZadYQk?=0hpnC5p4K5usOTJ!)Gdrl3OFz!ny$#yHq0!*(2M@UFx!fM)~5ky~4d6 zPPu&L&e-o{m@=13tDK6or;Z8J#N8ZBC8s|l0m9>P0&)%X=Y*hi-Czhuh=#dVn*J^>dKVh zC0h;WMOkXzd0B;Vc7+Btq7(hX<`6?q(o$cwRo;pR=P^a$O7aFF2_`cVb;WnjhEWfH z=#!|$;j5VX!r@IM^M*H*Ts(X`$&&n=K!#KO-d;9*ul6}WvUE80?(N0HscvtV41bH{ z(&6us%#Tsu9xG0moWF>*1aw$W6lowm$PGV@1)Mj07|DEu9l@|TG_>s799;(sJ@%Kn)NL9k6320tCnh0T{epM^)35O9exdxGIrG};;!0D+oKpK z;@ZKK#P##WR&WDlq`B6Jamf6BCi*2utq<*SP<6KK9cm0w%$Jod_hX9DcI&hcSmIw% z#0?s^j!nHP9lYtVy1AF==DuDvH@dHSzU{Wv1MZU3)dNm*=n87Ky-{_AL5=mug0|bs z20zB^?NKu!I!O)_KY&|`X-IF_U%?FPhUsb;-;W#m0rHeT1h)Lq{4)`MhEK*Hd&%$% zNlM^9ekp9T+0!=wi*X!#;N>oR;5e1N^)9nFPGwHpqX@Cld*s_7^b2x}oT2Wa5u~$r(dP77rUsC-qw2uINy{{xp0#Tmb(!d>#WV8or!l>2TH> z@C1i9Dw;9;YES~O4*#OEJZTO8it?E>{Oh0?`rjs5IQ)ks^M*gJJR|wfgThRBf|M9jTgOfEorPRZS069e1Q^1F@@`IZZ_#sfwSa?73=Q8>GTVRl5vUT{+5z ziE1k=Xo#}r?qVLYb!gS=a_pZYZ&B6j#14%a%N}NP0m{BMT+Jv^)z`E=_W)Z->?07} z*#CnKIl^XJ;QeX@a;)`8@ShVZ&H%lg9IQc2T}AD>%BuXvjg6SWp!BblkDwxk`Z_vZ zoCghuk-J*F|b`od~^w_ugp#1`y4()|Sy1;$K1j$}OV+ zl?K2zw?{RgxBxY#@UQZUW3h3gWBIW$bH>C9R_0$gd+XTI1*7u|#^$=IEf2-U%qGek zJlxW1_pi>ZS&Um<8_U1L&CYX6TV`(#-;h7HVD24ms05@%)4;x*w~ygbFLXX=j*wJSIu_6a`mjj)$Y36-PAl3H|Abe?MCtn#?F9!4AJI( z3G}YZTHIY%5=?G1Dv`NgpI2Nk-hJ3^$zL@NNM!i{9GktlU`8xfnD1_6h}kny%(`l~ zfAt)s_xarM`KwpX$OoQV?(O;E95*Y^{qt3`+-I-K3$JrC^V}cU)om-?eOJv|UA(de zb&WR3ALm|g&&t0eHYV3?wgF7{TL6cR8(k3I1v(au<34;PpyS++>}vOMtHu5D)wA4D zza?B+I2%}iNU?U#*vkCr?t!aY++ST;y*l^v`Sb3Wc}92{AlR@z3omvvTY%v=t}2Ec z#$w^!uhl~E>>rJexmDHf>3Qz=-P;SQ08%!)tqKXp#sIc0{}=9UKn*u|pqVfwz7mOO0o1Z_ruo8t_r@d!yA}Hao zS+m`ju4-v@U$<@#ug|}5ELyxk<$u+@vhn$^ZURKzgwX|KMgu2qrMskseZ~EqH4ADq zn8JZ=a#r-BbxoKSqfi!HTMe9)D92u7UNkzht^&G`jaAAaN{kFF&lH|O5W_8tgHz01 zpmF$XtJ3>N1AbA=3P)h9tw@jNNEmKnV;Jqz(xcNfKH0VIw$N6bLTpaigmtTt#SPs( zh=XthF^daiWO3V-p=9NJ$^N2qoqKI)Thi9#Eh(GBo4Ujupe|9!PmApo83(z%cQ|o`Ul7hFx=*($Bsv##OTcB=6?KVs zHz%Fyy4H65)P$(synhy@^c6;%5LfG+Diu&368O5R+!hD)Ww~d9Dk@ZEONr{FB5*&X z)KGmVu5XQIDi?lnI;PAv>rv+iSrKE7fXQ+pumL}A;&E=|nIhOL@oT`Z2|vOd*Wzc) zeXt+I?>YP!kE_^c@H6HOBv**?-iqHF_!;wOxJSW{!7qki4t_Xp_sm|{AH(k@{21>^ z{HU91jM>yuAF3HYW6ZuO0~Nydn7S5k*;KDp4sSN}*B6yfa z)uOeup`JP7^WRr~u4#m}a}&2vK4?{30TfCPRtkXbf+9Gzqq_nRQ{5UhR{>bBnw7w; zDr;O#P$aJY3`7`Vyx8k~fH3${WNkgrfz~PvB?SswzHEyVAU1_0h zl-hQMa#OHK#7@$JTUg@a;H^R9z{|t;%|Vi~9tyTPg*XBru^vHbDZ` z5>hRIxK&YGRkpT#V-s+l46>F+&tq6MJQ>>DS4=eMh*TK z2f$^Jx4OXGk{Jl_fv5rW6OMSD@F`TS&jMA8S6SGo;%6RHye14jd(9IWFL1=+BsHIn z=MEzFmG}&Y<$yIzc=MS32+t=x^F3qajfM-un{c5FFNm@*Pc$+7jGa8gJ**cCWIewr z0Xy9vN5-gEXpE6@E*!8#@k|5`ucGiQ2kXF;gNKPG4g=qXf^ofIjPaWbhoHQ7r|EK0 z9?NIs;XN89ZyYF1^C+x6v{n6OILf?CsB*-g{Rr1=5(96tum<*Ex*s6jHn=feEG@ir zTVM}Fw+hGTwJMyIF@9Hp3(DI$-7g0Yk34yMU^j6n;4#RH(jvzAT@OAe?X?zaI+)QRZD#IPZ9jG4g&22lTxL z;~pJz9Dbr)S@v5G65k=OCb2JThW9UB*KeP9w|1#qVO8@c%eq{n* zm%v{R-t18+uQ|bgYXbiz@Mgct@HZ#;-;u!Io50_nz;hfK!~8#P7*O(0<7f7@Oz-ss z|98Qgy)FHJpWtr?x&Y;+gExCzhVPr;pPj&u1J6@w?sXadyafN*34GkiqFH9=lv%R% z$ctGyfM^OXeSxW@(2x9Oo`z8p@TtI>O2bJhkf+j8fp^kCQ~vfyke~!7DzFRFC{%&% z2&@fj>nqf%bWlmdz( zFciKmQl(I$;sA-`R;cl?RE(q+mwIl^!UVSpZAjW5^-g+#bRf&=2RGS2;;{{hoKLW4o5uF5xAlT zCC1b$ao(&RIr=|xgNvA8KD%|-r5u{a?lIx&Fwej3x|XWFu&*z*g| zxe5whmcoKUIE4X)XV)A|oWv5^M4E>&Nm>B8q^Oq=;*f459jmdMNDJ{$BqmV|gMt2q zy+9$4SV?q`_SSp@=;>G!93+ng#EL$kh<^hq+)=FKV-;aCM^ji1D5_df6kSndT~R!~ z6rCcpSSVkLV7zjnH9{MNt{2)Y^ov4o63Tgq@%ITmC^QBAh3*{pq=SSK?~eRgLT3mi zt{vSgh4QT>@?R4A6`^+vRoFxDe^_{hJp}$u;T84}c!fO#n&N1AnL@LL76|2+ubJM3 zLN5_oBXqsc%|iL+BEvr_^aY{(;1Jz89!OKrH%a>n9WQi>(0M{v2(1@-mCzkRZxzbh zY07(0=u<*@9!B>*7-yuzg%%2(BD7d&sn9y1>qwDjg^dLIqPV{$?uUf_THOCf=s=8P zriT|t6+Mp>@uv#?g1B!H{yL$%#Qj@Be<<{)LSGj8fzZDSP2+e+dby<7J8+&Ly-?iC zNs)fF(ADDpFQf?9Ec_m!Un52MZwOV`Mv(uI_&+7|w?ZwXp|K36}A!RXmOt| zbb-(^p_mCogKNb3nxKnA4?!OZLkEBTFuR_Dvl+ZtilZNpa_=Eg0@aaqkfNUqb&PG!^qJ(;pyogwUBn7m`BG5}_-^y-w)O zLjRxme^2O-NKwB2-F5tIp(BLi-DM^B451g1A|K0z)(BlE^ctZ%gx)OlyFz~?^eLeV zvk3BjC%nVw1o+`XCkdTKit_Ojp`;gy`*NXo3f(XM4+wpR6y^J!_$v$|$e)XaIMZ1n zbTujD)(gE{+*^g>Q=cmQ?LzMp+6~Vs%E=HqL@2*&N%zI1kh@Ii7EP$==L z@O_026`CjXETQ}=C*?K?eNgBVLSGQNiSsnlMN*c7XCfYviZ2v?lJJX#=lYxBn}y#b z{9VG|Bm6VMKPUYA!haw45(Hk~Y)Bu2H_Xr~g zB-DP}+k-yN?@)p14;}tv`DY<+Bu1T|)_0=^?|`o2?sq?qI)~EWM*SS6vjTmTtOVJ| z)JMs&+;D2@x#+&3o>nSI60}Q17X#HtH)@Ea#4D0eBPFaAHYQJ`~sDbp%v(8J?a((TSi0D~Jm|nh00~JoJZ?N52 zp;ibs+`aG*%mDoCZ{v@{og7pbxM6|nLJ`9!IjXy)xI8I_BEm&*gp$H4goKj9JdzY= z2Z-kv3MFB|XE`rVW4oEDS0aR!N_7g`O8N%<`yIgFlq9$e!!PWP#Iy?M+*ELs*+Onq z4gNZKl{KxNQIkUq--9bPe9aku&b7~BESM;M_VRRmcEqyJVJy4Y>S^Cb%QQO*QiPwq zyvOy7#+S(LIX3NdW{uj*BLnF=lcjWaMIwC^r}96)*B*wrkm30SIXM5pZtofs;+76#=zZE+?`>XA}aY)O3mRBnIy3yZujpntkN+vFMrYhC_m3+Si*w$+-RLS)Drb0`V zw--_6Uf;nm&x-Xt>C(mh44l>HSk$MJtn+8ioo0kv|2Hpas%rBpPx?D2pU#&}y1s)6 zc5>78aZC|+rcS)+7~D7(F>bsw^ zdG}z=$oP~;YUGu}ZsNdd)xv}8MPp2PFNH%;9&xozJ@c7wmya?P_URXVO>bVG>d8auId*bVi5b(6} zcc)MgW$s2Ja6N5|iE=L-&;$*}{Y`|!(VAyWxL$BEeTtrcLbwOLP<|O1-U+ym#AGS(GePzP##U{?2beat#d`5gNZr##JqI2B z>;O-BMFE~|yCJ}PR#Z}xWsPMT$V?xzg zDg37kKS$^Sp-Y993H`j#bwXQ&a>dW|zAkj1(8q=TOy~x;TimA#e}T{i;(n>{i}L! z$55yg^#sO9B-*wced{_qM$D`}JZi(Bn{TkOx_>RYw}bdte+CW*F^Bp9%gwQgiL=?m zx*--4zJk+@G^kY(X+j+BMWB=q1X+; z0-1WqirxSm4Y%C|9-Lcr{W})0THJ<~cGgwMf*-IjRQ^$)nAf{oMQ}qH;Ob#yE+S^h zK&xnTX9#5qV(>9cD7LA$ZMF5WVw*D={rhs+V-HLt%7*5T|OEdBUSyAK_ zxFTWja7TJuJ0a&Vu;boC7d$q8M&P_6cuedFp>P8bf1;7$1AAm@V2{4TQAhp!Q0JxbP{n!#BNzW+^vTCZ=vFl9cp}z_b9D;CBg+q{KBq`2K^iLAMq!7Op zfKvd|3k(x^ANtbA!NW(bjX0Zc!GjH!!+23XL9IH50 z+lzYMiIDbg6?dUdWw%bH&pq_Hk#%Wb9AT1sn513O=OL1>@Y!J(NBAzQeUmN^%CHmJ zPYyAqjBl|d43$Y+rphFagkbO6kB7GQML%H(oL21t)%St=izfD;(tE(eMj&(n%l=!o z>()^sHOuc^z=%ge8=DbHV7QITJ^cFowj`nsyIRb}Nh`Q!5n^9u7tFY0KL zpx~d6cY`5eY(ZW@VZ+)gVJWoY(}|sLG`q}UP3c;#mvyw$B~L30k26o}>IEn}PtUuk zFn{cXl?N`uq0`veg?EgtaaYfBFRzAvP(Q6p)Br~HgYM3Z{MECz%|39)xZLq$ z+UlUIl}{C!SpL{CP_&s1brc+1jfR>|LCi_!sg+*C1$9ce_ArJWdRltg9I9Vzb~d>g z;z4(=P0Tm$v&5ZU_(SEMq0X+1KF67GW1q3CCqTFw@>VF;Gvs8Qip40$Q*de8^+}eQ zGAZ&?Jjn{=<1;wa;&v6TpfP-jiZmjyu&M9DJJyaek#Vq`Tlxi?J9hy4exE^h4drN$xfY* z&A%|Otf^^jP5C;$X8s8bUfbb>to8?tR)}A4=%~pCZi(k(VQjdI;Lh>R_zcJMpkczB zGlemFP{UOlhNT&eAHyFn9QQRQ4y+dCO$ay`>y(6W~5)dH>MM zglADL>8)@XJ)HN`{CQeqzKP;ar*1|#z{KP85tT@osoeKgRqq+XoqHGhFBHlva`M|qF=wmYENGj!zaaEA@mKE^!vAe?PlKKD`;p>zn$V%* zPMu}?pCfdZ(2IpO2>qT=b4G_Iw7AnpQz^NszQGp?KSTWI2+!vS*33towbiU?Shv=H zJ>|IXr%aqQZc*`F)gU1}3k&f)khu_mA@jO{h%z_-DUW z*n7>d-hV4eo!$TA6(xsS^lgjN2 z>GL>Z;jp!lc6VsX=Rn=)Wc*F}SNOO?=wXFh@#i}Hy3yjASfTAqGTi-s263}fe?!mH znIO(MQ*S3Xv_x?s%Y6Wey4kzvxR8>Ejf6EVGK-?hWKiGD5VEAejbIjzmuCf z8Ppj?7l=$RU2Qp|wfs=(&*&vwIQ0^&nGKhgdO^T1l3u#Oa>h(oGBT{5p?0Jd=Hvh8 zu&5H@w6h(26=LN$1*sd6KWCgy$4cG6h~qUEN^PNEq2|J=3a|JK&84MAnTH9Qi=@tB z9wuroBlQj&+$7EQPvr{VIa704slTS*S(=Mlkz6G4I(}i>nVR|o`c0z~>vR_5O(*AO zH`}Sb>3ji;0oF++8k#dpvw#!{p9$}0Oc?=f+YBCeHBj$$7V4T^L?;TKNWmBDEQC^T zVWLa6D8Fzj^WrRJ1-aQ+CG`wZZkUIxH{qu$a4=r{>Z#6Md;An`-(U?pRjH}WP&Hkc z#y6P8N=XA^*r8mtT&f=rYmdJ0zYo7<&NtFa8!YEvC1N^`-JE+QUV7H3t8Hz)OB=H+S46{o7D=CeEuaI#BM z)k5@n6(W1FzSwY*db1&IM(D%R^bEb99l*hBqM0z2F@M9|#cmGOben!uljJ%cfN zhS=$Pvj%LU_G-DxCUU7U(wlYSr;AuO!XeBudh-T)BtMkx@z^`DEUj>}Le8_9ZES{# z4yB^IWb!2*=WIvmYh;HqFQutq+IHLNpDd?#gohk?&`_J-+ub|xXmotciFf{HSKz}(G}z|%4p(Ra4^ z&dA)!bmutttGIJA^T=K#fr~T$#3B?sYOH0KWWGw@wGLrSo$SS#9GT8Kha=L>e$vVO z3%xIQIN03mXPnHJnes-5`WSBZ8%dc;(da6N-|BX=+mkY*^uOA9N%_B@geG8ZC#ijo zYrhIHeb*uEe7B%e(w7t z)Ka=L)Xw}Zt1H`9p8$uL!;F_BY;vZ$E*xe*qY_E8!nE+GW(~&*y~`>|>G$6_kb2Hp z^a)*yDCwo=qi;R1zunn1KGj6G{8#0G6)CvdRI73x8+nG<1 zFLMSeew&@SnzGAP%5HX>ok`3hr$R+{v+uGqdH3j43V)xS`3J_Y5`Mp(c^#9h7XE;p z*}?cLg@4S(!xbrUFsf=BV1>SeY73`?nE}r^#R`uK^8KuS^TB`MT;^oE(;rnwOeSoeyfMXTS^dc&8z#?YyGKXne;k)qH%SnHH z1r)s9hr=74hMqk`96q>D0+EhhuUG}Y z@@n;3HB_oA%4(Zx)&SuL8qP>l#XnfQJMXo7tT-#K?zM|2TbZy}zOWlH@ zz&NIBs$X47^dYOXYE9Kzphr~zOQ+)UCWX&Lq$sO&&P7va0|#sB!ubnkE}C9ig6K8% z-;Q6Yjfttnko1E?n*1CU4hAd$z0wKYt(x{i_%Y<*=} z@QrtF2;f<^b|w4+tz25xSm9Ma3bGz;gMNc-pr*8>nnq&GdoRG3A~zdqnqm-FgV*!* zo97VSw6OtKKc&?T^^GU>j(#c71uIq~DoS2izYg@s&ZkP&SjD4L6meBOR#@I^?dm0W zG%>C)SWLuGVnucFmOL9%HDg76{c60zP+ATML^N?N zaE(^XsRLAy(!vA8fzmEnoLM*Ju<{Ac2q1;dkps9)tWxBKpWg}u!9oDAoz8s*GKBZ=*DmY-f zR5sR>vtOHDhaQezh~UPdu?i4osu1k(to@BDSGqHxU{zJ`4u0pgK4rbwc`bnN`LfZwjm?3n0lQ8i^61$l+RXbtPi6~={$ ziV#*_Mg5vJd|c%rqbGMM%M#<^8FwcO)-&{@;hKnc^HiAxdl<_DWAFe`W(fY`0ppnx zybU-K9IhfI-G~zxKIx1xe&@m= z;A~xi{IML&AD1sCe{*3sabUIfr{fMV;9#u0@1R{jTY)QqyS-phjD0D>1k>F#NY?|? zrMpSD8g|MXhab(WUC5*VM*K`3814;(JChbM#%~??V7l7_>Ee{jOZQsX8He>vlNmvo zP3FPVkG~s)f++J;M%QEr9pyOHuhcSy&Srk{Bf9>K1KJ_5$@Zxh%wX-8L$ z06&0>TzB_b*t%Ig3=h$ET*)avH6eUre%P|UIZ|f;oh4wXLoh&imZTTbA449D<{{r( za^d~{xJ6zUPwv2)gYg|S2**4rb4FT7%ELeeAkVRP-1|w4L3KX~n#=H@N)9MbGRaR7 zI#=j&p{s;$68c4E9rf zFVT`$_l}_I><#oDalcn}84=y^hC2)$US!YM)cEy90U=fd^>SB@a z&chMV0YXO#ohbBtp$mmp3T+hHDs;EduM7R2&_{*7DD<$<5C$~UEfhLc=oLb@3GEd6 zTT(pielL6o4<+I)q?1C>sr0m$%gW7zo znMtqj!pXA4oVQ z+2-TN9;NP+U{mSa%Tw?TyAjp&DNe!R!-yfY&a=>)QMdY**u_dJ(8zgGfy)o_|bj)9#-j z=r|{LN}&scm$%t@D+0SxZo|CY^(WVD_x5^f1FQt8Kb||~$1z=OYyG%!Ay0bK#7DAh znz$MZZkq6PJjS+(8z#MVI^k`T+GDA0l)rWQUswDdj8b#hV!Bd>Ym#7}($mb5@3p>x zxe))=`;3gwaO8*KM_#5wYYyx|LqD3tJ=D>{#SqrSVLXQ8zQq{hHw6yHPCw2{96`ny zzjNV$>Dn_qO}q&l$0O^&lw&^ZCJwCDVlbD}BF6a51s{}m1sGE=q()vj>?RIHJO^eZ zEn-YPvur_mo4{}qW&SwP8F`Jc2jzVPrmHgHKm6L;B*O&!*q-oeFkaPQ1{o$0 zE^$w`3E>{>A{^63mOQf;Yl8?Jv4!)nfm!b?D@1taAR5)|H|R(?U74W!@%LoG6qugr z4O;Sv?8$;{l9;Q{;yqUO9U9IReD4&?BhRqjyyqd_Db-ISJXb1K@$sw1M-~Ecyd8Jn zgoM<-2{f1CL1RMI`<&o8rs+Oi=p3O7gsu>(p8W{N^9P2%M(B1@bcUTmR|(~qb5OA? zBmGr=@yAe>lkU7=2rToCyUrUwvG9c7Tbzb|2@|)@Yk+=iE%aY&R{#$yR^2{z9`t#O z+72gKB^_AOj|x4!=N+RHYZq-gM0e=bo<*J4vk~WP#Qh}k8bnW5 z(?m#(=$EAU+=AOb!XJmX&rmR3Ooy;j$M8wc4icka>U<18XqAqKBeY5_{!AvjX&-As!WW+s>q58Yh77Y6T_eI2xZ0Fa3~k1oVYyz=M`hvTETBK89%zuN$@)hyzZ3i zP+`sB0Qn4sOWa$i8=Lx z7=6JPEDgiNiK2tPul4tAN(kfgX2~FXb+H9Hwbwb>`=YM)I>DZAR$aVT>c*iUCI<)F zMX+b!hXLoUaTE7CJWmZ8!zS)^juwvV8WV?d815?pfyQr2LbyBN!I{7qV@=%aRPtaR zo-7B~Ff4Bvzr?*x29~40fV(k9UgBP-5(|FDr#w<4uN-z02Ud&cr88&|W9m6^uTu+o zQG}tqW9@Z_56!X}W7108>ue9?k8`id-&WX79LiV=k0>o-j9)AGU_ZD4^0?+>{$Taw zCGK^uhrBi;1mS4q?@pm0%8WxJOfn3piITY2`8>i6>LOg?US}J^oycBiKPvYi660Qn z>vq-)%f+$=cOp%ww7qaM#^lfNh6dgL9(x`3HQ(4u4~7d1ZSW2nEZYo=T1|!hnE>xu zVfiffM(i6j%mepEQ8Hp!&%+G^8pV&#b7P{g6dMMVd_@A^1fH_FE};A^0e{Nh9^j*} z-RY>!0D&J`;4Q@MS_*32hYm1)*#&#@j*aK!;f9P2#>^=tJWE6QNIt z`v*e#e4w1D&`Cmzg{~56mC{4q2l?llcK;%=(;wz$W;$Cy}1 z^k|yn>*ei4yTZinZ+lu}e@*P@9AZ30cPo78uyuWB7`RPo(H(JE(h*=z89eE9iASuZ zaAap^7#PtChYFa}#BpM{F87(I^n8e0jiZC3==sF&vuE}(7*1L7S3#$k??_r&}m>?ts&HdiE9m^$B}q{;8yd#*bQn3xub_@iK#p2C0tl((iko+ zm1hcuQyN05Gnz?%vBmfZcszW4QdFv-_%0FsihR@X$W1+>WI@2ay<>9M_DW1 z*75qMp2c{chENaY!P5}Bg6-yM2z`%!o`%q0881#l$c@twa^p0FTu(!&7vp&vLe#c# z<1~a^PebSs(|bzib581J#`83Ua+otuLx`&7?(+|;>V$^S2t+&q4WTy?>>o=*=tkrS z+KJC*zQUsW8bYgCOGXDMl({^}hBJS}nlZj z($^4brLV6cw4A=ahEOBZ^)-Z^VM4x!(ErE4zJ?I*v|V3A=t{=*HH22v*Vho@$aDi5 zLah`X&=9(Ug$QT}O`)`ahR_J|M`{R7VQ~T)LRAox=0ZbAO|8C$&?qMFYX}Wtv}0%p zaiNy(LPO|72J|(AE~gk@Lx}2n?lClk)OVRgL&$}O&=iLEHH5g&a(xY)IxzG^eYhJFeA@l~*|D+m1 z=&A!aR?$pKLx?JXko}ox2)P9Nb4K&)LuvSi2C{1>yZ?ehJ2h{^?X>Aw1KAfL1NI1e z2%Kpq;b$+;s$K)|#i-K$+Z8^25HS5MdnCiy#e?y2aXa@T2(V7$rLIjSV|$8{z}Q?# zK>{^o?6atUWEX33XK8UE5jQm;ZkiGoU4SgQgIyKLwk`nY=r31%T5rr$*3^HB$MGk3 z<{Q*^vdj+0oasZWLTe-$l_k`zr?Lb)H9+*YEIgGZw6S*qFx+;#16XjD+dXfyn|?c( z&GH)15qAJlMiawk!yUl1^o%bhwc0JtHEwfgPI`auUZ4IsDoh%$3PL)JrT3{y#@Epi zT>PXw7Q0rdFbbZ!>*Ig3>ZXj+fTqLA)hrbu>ht|7g*Hb}Wy2`xs#wZk*MwApS#DO+Iw1CN>d|2Q7K-pgBl`K)bT|n9>I%}hSs0))Oob(SezCP zG_bU)jp*oj2l{b(OrrTAS}j*urcnfmGCtZmOOi;05yRE5*dXCWp3TOuUN@+)`yeTy;uLAVVj?!uK?`;A~ zMx~8PpT|>>!||0?ViMgRjD&z{l&@-(s51mLEu#n}T2ESw$kVtIT|c8hsFj*nMx{+> zblU>4Zk*Ql)sd{l@(K=bDuF6jKs?6%@ zt}OVNz;Z>I)9@D$8PD9)U!O1XtPu+b?~Ww#u570lLt7Y@Ch@N9XyIZAYvM2-!+km6 zV60OT!g1xyr=&3^T;g5XL6l=83dwTtJe1|-S}XCc>=9fp%r-*cZ>)1+4%qq*pvSK0VnHA_S7kHd6 zFW{uCCLJ<=kGnG7efdUz-=)xV(05~gy#{l~^08<6=F|9|3+s@EY4YxhFDn_tdd)Tr zDEWN+7&Zq#hCd6`82X!~E?&pUQQvPd51h-ZjV*5ZC5~7OmwF+%GLAx27WxG1#=)J$ z9P(&%br*t@agGTmMdyikHtg4=Xm+IoqVA5+(Q}1Ya=?F1_$fj;FHmP>xzJTYHwpcs z&|8GwBlHJC9})U*LSGl!DfB;ts=Fbib4s^M*Y_#iE?p0&bh~st#OZcnocsX~KaR_8 znAk}(NIAZR&KCDY!Y>tiqqyf`>h$IXOuA}bKzzo2h&nRP9KLhd8HOn_Y*&fo%VC(uI$kaXkPwwzX zk-q2I;97c^?b z!eKOi3~$!RM++B27!wCp^`7A6w1_c&Q@|TLy?%_JexI3~@0YlbBf8gO__NHMb4@ws z2jakLO@wg1XJd>>i_-?Cc+U{ki{+v`QX{V%b`u9y^=*hKEn-YPC+_1SkjG~L_6Cf`tc_T4-Z-qTrUVe1j*(uV|myfq%3Pt z-X=8a5V#p*F4kNlmG?i@nu{IbsB5m=zj4h4;i%8!uerExV%WcR?WGH2hTbQ? zMLD(R!d~K}fyqv-xlXOQIEik>FZg6WwdOjt=HgR;=0v`IZqCY&x#rSSy_|i^1|3CQ|msb2x z&8Uy8%tD--MK$2{^67lC{_=^T^pRD<@<~96c)j|mof z#3TxyA2GB3`B5>rA^3?v*t%ZJn2MnP-?)a6jGGic`5NZ$e`NH}fCt2LAa-Hym1qj_ z3%05`$6#+@IJ0-227A!ZkA{0UJ`0Z*ZVbYhIIvojH=PzS#*gPa#!jzm@ni2a#`v8D zhu}HJttt9!iRwn!dV1#=-jEdmuYq=T{xl2jL3wXO9?L~}uzK=%zGLzLt3_R`C@o@4 z{VfF_l=nV_u%0Q8?hHryJl_e*YXH;LxyWjS3(EU*Ab+@i@Z_~7$oo=4{!Cg|C&&vS zGbVr7ihJ^|7kM#In(shBv$E+wl*iv~LbWS!evWbu^bG_+UuiJmh^60Ec@xippdw=3 zZz6%3a*)EyGn?44;mBudPyATVEEns}^if!?O{l-Vw1_eC3~y-A{rKlV!JII?V2b2T z@6ghZ#QjN8x~wK`zmz6?um?~cTi!RWkIbqZT~z}?f7XO=T)gqXGd~*o@Z85d0`xhL znVnDYOa~7bb*2M40`jPfn0TgBLw8h@I>SM6%sm>Lt;=r)HkQ|x)vf*n>xqfuqRJd! zH?c@JPuzN<8`cw1s~E$ytq;}|?q{~9U!*!GTOKEpMIuJHIZ z#c;dz_-hIlsjD@G63DW4iA`cEg8qNon!;qfM)#SMxQjZqrZ}08XLq%x2)3%3^@gB@ zIiH$Ykv%$S%v{U4gU`m}g-cvhP~M5GDRv-SF@DCFbQ9MUZFojd=JD1PZz9ZztSR1w zJeKQtYl;$ND2i~#nEFdxQ}El_oVSm+rf7n^uAZ@pYYJx0U_9^EEF5>jcn6y}7_sauRAeOjLm%{+ z0N#P6@eV@7F941~n!zvtenAL021mm?P~lD3Q+NmZtEhr%@QJ@`dbA^<&kpZkH>Mi< zH<&{VST?^$DR>7wV*=j6chO^jcW@s(4Bo+A^zrcysDmqb2j8X7QSc6QtWY)t1@I22 z8}8#BJVb7236fNJ2TRCmyaRq$&BHs0f)TufpQ^m5FQQ%zEAb9KMy#XY9o$AQ5AT2| zi-t=}9U0(G9PfZ2Sj0QH2YL4K4nANy9^Sz(=;z@b@Dm|E-oYGX40(75 zwG`yx9WXDS4DVnzq!I7ndB*hc4ip?iBHjT%MHs+4s08~7@D2trqaNNt39I8Mcn4Q9 zD<0m#hvYoG1Aa=;$2(|YyLosA3Xj3VJ9vQUodn*&&zPQvcd(uLJPO{yJIsrRcW@rl zdwK|BJK4plZ;|ux4wPoOk9RN;5l;Z`fOrc3Sa=6_BS#AF;Fm1Ak9V+&wPX+tLYYg0 zY&dg0>%#b^Wo~3#AMaok>)XdWID>J0yaR=3;Nu;<&j$DL4t7(Nk9WW?hXn8r#xh+W z?_diP^6?IS&%i$3!A}_2$2;H)VgbAZev2f4cfgSu#5*{Lq62sb8(D||-oc;gAHX~4 zAb%v@0l!cZ#5*X57=?GBCIBDr;B98k$2(AnEXTk*7>d*s-oYOz-^V*pXdyn{fkLx6 z2Hrsog*IPACEmdWEToTjkVMwUJK!5)0lWi#g*SkAu$3k7@eY1WUmx$_HTwE^2er($ zk9UA$q#)kGEo6PXgL;Yz;2q3jJ_2|L)berzcn6D_Rsiqdixd~YJGg-U0lb6r=^wy5 zSV;a@cn8%8@Ja9vUSfuQyn{VV|77qEI93hb0U-)MGrR+SsNKUm_z6l2yn{19f_Mj0 z5yl>F4}micw{1tkI~c(*f_E@7fOlY{IChbez}UP!Gk6DQ2JsHgjK@2e91u4};~n&1 zje2+o=Yw-JrrM|TTDa1OKibjNNx%=E+#y&z-a(_<;ixk_Wbbc;I~W#(JLuuV9dKL* z?-!%sjbW3W4A#L2#6#DC@vsh5Sp@1p*-rxL;1otd;Jat|!fBcM{Hp#^=RW~P!O0C7 zHE^NJAY-Kqpabp4FL?T&=lwr~cc8ORUC4hnmf55LF zb@iSmml4KbjigMXZcKB3eJ{!LODq=Dtu4>RhY62`i84Ed8p5zNqw!<-T?o-&05Pe1qir6hgVy$MTc_=`o$~*_z4tn2-^)#Md8lZ!f4NzE?Z?{Bv(MUlt+O`f0ej7Z z;uGh6za0a|*@J1o@4R0yu-m}j7}$SDt?r-xJ8E@5yf5yk#e6j>E~h#%eG)S>qrZyx?>I|dUnC~%#sqJ0If_rDzjJrUFyQzknG z6AtT}Iz^nl`VQ-iNh9#y<-GPkXFYg*^!{(^`oF1joY`Ch)V$UIz5~#2$3W}3+k2}x z5bhnhx9V-L*8fdi=Dk|K9Ru#|xSI`J2)io@;k@Er>$o6V?RmM`QVfi5PcCiYM|$qr zjxIX}we_p4D`@+=pXW%~j>1m;Xtra(k+pcP;6pr)eZ~**-v2z8o(MhiT!&}JKoMuJ zzQa71Gy;ZxIj{S9uEsj2Bsf2BURS-gsTpS zo~xhd>bD)G7A^Jew@;hvDH+IwS zL3Q;L6IKiIIMo2hp%#_PqjP=s#ZY&020kp2%7=Me#a*g&xu>htSQ4&8waBm0y(%U*_{O_O&?4cX&LVw6wlem)y_O9X;o0_Gk+~ zpRQ+G1{oOFV9qT*;!GF8FI~@c1;T8{v~=lurah%g=joU(8=me3q)Xx_PRczK0k$l! z97r?5*beHUNjnz9FL~f~T7W!3k8u*mMaibO8wmS>^azDsHT;qXUZ)ffBiklU`k5=F zP45Rl;s_(X-tH`t>HeycP7~LJUyo9iV3ldNL27;^VjX^Cpk9 z@#~=XxiosDd7nuqQFCwz7U)9;)eg3cV`0Or|ykn&1>h`bwmrcj5noW4to_18|{rNucUILB(NpnmV^ zfOkUgw6AlvA{5Q~@n`f*Hd zo}OkPzn1h6UTN@J1F4hE@LLVsZo*l2+6^0Fa8)1RTu(_?)eHXws(K?FO%P%_>d&Dg#q^Fl>~B z2NPaPV8C;=Hdocuz3V&|_Jl_>&xPJ}^w}IYww8f-at8;cmv#;*y)W)G)Dxjcp6l@R z(iCxYMdougo98;b^E7FMJaYA|y5xSItDooM?31@dKhG7MSM_$D)~}bwS);$Xw$H!% zc`o*fJWce!XFQiI`NDJQ1z%nOsMpcQbFFJ`eh>80Fj_}5&qXnT=D9eT+`kLGG(8b| zw?seB)z5QrKBUVq`|puvuV_bG zIep16B`x85A)|y?M3#Etx%BHp7Jk(0=;OJrtZG=hu2BxU`uzTKS|X9{9Yp0}H6kf` zY4Ng&uRG<*j=$~S|0^e+>~NgLT{#Y5d9n-ph&&gNc)aY}`*=SQ)8#nj9sj$3f8HPW z?|6P9 zjpVpX3CFla0CZ*ext9z8b5le~vI{Ztvw)uS2>%}$yRAyZ){}Vd5bU;MYMXhYcrsX* ztgTyFvRe07j_S2gky|q9r*&QgT69{!%(kCupJbJxopT3AZgsEs9O@0}PX8o)7vd)+ z!JC;uD^oH0-{CQ4ev{U%r4`J&+IT&_IrJ-V@O)ZB)7n*vhEGsg&bSg}E7}|t>nC~l z?_=U;m>-0D9y8fKv?qmLb{sXgsGlP)9|+f9byf|?blhB}>!h_I%y#PGM?ktxT2JZH z`8lS$*di$OxMYZ*(LchEPk}gzOV>#og?6k*BDRBa3u(t<_$3d#4(%T9rN=mlS+koTR;jY2K;r(JKrW2b?+YD_yQ}HZAA9QOW)w0dkZn}9a!ho9 zl4AmnM?UI?F+bsS1LqjHz`#WYe%!!XLd?_E2JSYH&$5S!z&7@-TZTsQ9`2?|immID z|C^Ehoz}O&W+Pk*|F<7^P{tDXe$I*C`8g-{0zO4Z;#Lgz4)S_Td`SG=5GM*I4vXQ3 z9D>}Q?m&v9a(n|zHIu0F<^{-E`7<#BQ5P36kz9Tes7~VM;hT3q?{#Bsj` zL^wqDIygiwe&7(dF~)F+8yJ%tGJtOdHxl}r4#a=hxeR?p1?GfX00-3JU+6AI!QPk1`cVZ1(kHy3Lec|*Qe_zVcobdte^-&hLVcIp{IKsxW- zQ#!u?BoFB@-Bx;xlepQy#m}hU;>V{=oWz}p0J|Lv(T-gRV>^1|oqr4hV}uCOiZ`8i z4&%Kb{X!`HQf=jd*ZGs@IB|N6lYYJwxLv<|(2F6A_2cV9=(WNxb$}Ol)MdvzkFx5= zbxP>nm`0Dcc*PMWPRiPd0K0x;t@`!GJ6{NTCkYYIpqIb<33Ab^tPVfaC! zS)zF1-3fwN>A-5G@VYM`T{gV)Uolv56h_{e{mp(MWLr_L??zz|PWG{&epZn3O1+7^ z7rZlhTfSJdvunfla3%%rNZ1J$xCy+5XXM>UwN8$R+hl^8tXs&{wT+{r3utbacAvsJesi__KIfW5|oDXjHCVXY6~zMb!(5vVAf=Cvm% zzvEmE=z=TvdF|Cqk`uZJ0K7IA5aG4=GA1`r%vJG`$KfYWJ#aq$fv0{IAatyv<2t_p;yK^p z|D%(q=Cdl77^B@bSDc9ze$sJ6CoKqFToO8|hK1{w-8XhpiKIcu((aCG8%BC>M{rvEeeu-3 z2@@xA>HP4EXva1rVmo@{hra~^T|x{{=%w?+oA3^lej${8skZWv%sp7!$I)Y)^m95t zd<*EskcRbx*Qb}x5AOjz?rp?L{nF2p@3HE~HB0K3&JTBiUR;O)3cZc+^W7ot0jqw! z@xvhuG>>7#(aYa`21@uSRA`1ER@}qzgG94L(dI`s{BR4>Wy25OgTdmNQeS7uJd2M@ zB>>rmOwJB}FZf}*PVWT&D`&}ED>9uW`>!?s>{z{M5Uk-? z^_+M)=_0@4djVG(ywSim12-6`_+QNZ+lT|+XTra2;15kWpT{iEm$gmmZ28@H;Cwm$ zfjD1QI})-aV0O972+o)J5DL%xX~yJ+47eCr!1DqLcwYN_c^@NkLp*Sn^W}Q@E%rA~ zRGlwt_Sc**YxcMI^JVhFe^2MjVuBrmVsp}coG<^d*`sg`d2D7ef74X(6 zJZlPam~6cCdiLkl2lCJ<2NNHiGv|!>II#cY<5NpYOG@M8pgvJo)mk?`K5h=q(w8)? zYpJPgxhg(u@pD=??I|ICk>y)%!)!0^5 z4cOMA;cD@HFtjtsgYn(Z_ZnR(esOw?13RKzIzQcizO4HtouBSMU)K8xzWq3_61tn5 z{_|zi5de^%2Z+M4Q?T5j+)GJ*G;prcFFD%Imu1Z#Yi^n}Z<~~5{O|C5S#ug$^4n9V zOq-$sLTEByo)-}njRGgctVnU4*>LH7cvD#LlxnPM^&+bxw$AKP-47ni#6rUc+*?70VKJRiU$7Ti03>RQ=#j!~4&rkKDPmJwbZnqi04PXE!Dg&u1{*{qxO}3@)6( zo4{?yv~=m`(mkakA0~O2kLkE(iIce52oOKxYJl*G5GQfz=hA;gJNUj}JGhr%dn5QQ z#!vFV>-+)81S>$i>3sAiFf`0hdW1rcPp0I7*BOfr8b^AhbHzHu51Oh%_)la=D5XN?=^U*hh-qTh-(&O(w10{;* z%`*hC;?mEhZ$Y|j&ZQqlx@^v+Uqq$bQ6Zj7Lrp;S3;UgIMb&+`1A}mrkEM>O6`V5G z@5Q+^&y7d;6>`r0lz3{vf=hmXP!L=ll-UinRaqQ<3K>&$QPs7X7@cC&tpI33r z&l5>;#A^isB+hebam23>1d#YOX}JGxvh_^#dloO;^;$g}KJ}3s6Dw_JdgMIb>>3c_ zC)Sok5u(k?!=sH*(?d&~%&x5|z@tC$ zG6Uxt$T>0!gI7ri^@r7jSbUlZ@o=msJjQW05FV?ag;A)~7UH-l@-D)Wn(rQk!xo6oMs(W5l;YA)c6oQ`R{U zI_%V=ah>Fy)So$|y+r2iLtoz{vRmi|;ioL!i^e%O7T7O*4Iu5CM1)WxU;yU!|f^k`Td4==1S@y=+bG)Xl}a&!K-1zCq-C!BVBx zBTj%){Ok##mk!4%LO9c@=MRwUxa}B6M;+4$eto6m`$O`;tLR-!k8u)5ep>vDx*tEz zY;h8I1_H=SiEBe8)&s#S#=+~r=R-dRzvO{eeJJuAJ;q5K*L|Dbo1n*bkzOu->=)wX zrzH=(&YM8u^cW}ocM))#-rLB`wV(7DF7)_5vFYXGVa~*NuQB-mlnaB1)DN#4pWa6J zZF;AIUR>qS|0It5h+V&YAW}ar(o(-W;Afr$est%8-ecOU{xgohtp=(<{2PD?F+&h5 z?o03^s#&7=4PrKY_X?!jDVZ#KEL*RCI&uXE3xS-|79_>OT zl2pYS8A`JW;U=0pokVgf&xYSc{)_B6-I3wH&T@-1ImPyC{_D)s5cIyW38Cjm&-~Y! zunF;4HZ~#HvAx=a(0ej=7j`XXsS1_>85hR;JkKHXV!EuuPu&ZhT}RX@J#rGDw>r4^tT zN4z*GYa;?|dfTk};dsKQ_j&l4hvP?g4d`X!+wMc0&}Ody@ z;Eu$1Y{Iv3t;nRuTND(h<}dj=S=pg?AQ(OV8KFKJ)Xxodd?$d$`^+YUzF#v-9fB#P z*`KLR&d)5X zj#VrTwj!%_wpKF9+%|cjO$5F-#IXZ<RO!=@{qO4q-#}6)!J1I(kN70osycS#>P6Fik3hV_E0PqWajPUVYR19 zI&ZfZ?>O=(1>h!yAPofKVk)p zlRBmIcD&P4`h`$ql~>9XPNnvpIW-tHdoc3FL+g+VRyTOag` z6{NhJ^fW zBK)ce-(}!S1|EVvFMYS^d&qCc-tYYOYIMHKIW7D)M-}`w2OHXqe`JiYpPP~2{u`5P z6-O^8^gCcsakT8`{(=N@LpHzNr~TZ1e!K3+O`@Y`pp z{q}P;e|)3WN9?xgU;*Rw7$^O=5xCud{q}REhf<;R7en8Rv*@Fe-{z{F!fzkAjQ<_- z+xlMH_mJPl4Oj2)`zqcmetV8vWS;ssV5&Va#cyY0PwemQd|OZV15|ALIN#oPxc09)bua+=E zJG`IYMu*^(MBYEXJ+TbS_}?MFt?x9>mf!wh=>jk1qXKb)e=>a={0H-${S74W{?`ST zd#$l-*oVIFfG)5$r-5F}c+MM$#L{$u5A+IR4(7+5!Sm3?&%U&rBS`sV(F{GFFQ zDSFa(=+_12Bjq^h`VRfNz?t?K?I5yM0UbLhFCWrEDW5$~tGo%Q~xIBMIMebq#cu zU^|xV0@HAJ4(=a=t)y%5Z5QFYX)}qR-_ae5pF1D_M9biI_`%ElD*)659%78qGJsbU z3ZPe@jwWF-&{E1QSY!$pHGDa&)tC9|Y4$%GMUo zAM7(dc}O`gWTq6s&vX#2DpzTs0LIasj33wLzS5}&mKltz1iV-fK%qyOxA+;&Z-O|x z#Yx;52(ZtZ?m{J~e@5Fxz1dDW4FrB9!~lgJb=+)vF(7Og>E+_bej$D-ev$`XCjx!8 zI6cNm|6K&!rbinAF@%vG!-ZZG{5HLDY4jv+jmZa~+&HU#QG^IRp20~TCY;v;J?iM$ z^-Eaw<60~9(rqWL2EA{SuyHJlzjWJ4Ct*U&kVGa8(J$eLsAl@eiX`ss4mo@OdoI*?6n}+!9{BBiiJSSd z)=;X|`tVpM6p2R9#L(u*RjmVxKr#12&U~yM5yTJH@dJZ*Hv4`-(yjN3) zJw~@2KNaB3qwk~qcZ#xM37`H(_mU8plJP3K+zFE-2Du%%vWnm&7`$TMRMZglF6mbEp-MxKx5KpEzo+A+Nuu9QU61y zxaKQ*_Fi^2Dz8FHjz@i~|6pWhwWRZr);T;5$!~>UW`$}@#Ub!&>R{~Mw>$YkJ_EA{ zT2}buG3fSc{Z+hg@Ym<}@O%Jmr=D5_oQfaw%NwPqbP1%9Jn$+Tl~>SXoW#urE`COR z2S3hiaS}&<%x=dHRD!Ds+syX?+sk^T+d7&K0=p3|PUxkdfzf6o^OGK-(5r@D^1!RU zhmX@^ob)sKF}r@lf$*J1dOXPxdTOx(E{?X7DBl(*Wsx7V>5aAO$2CIe-3Y(rA&u#v z7pKQKiQ5R=rZ>T=UvJ;TUkG})2{Ay@bfV*x!YNO`3kB7O5&b_py`W`m}B zAYa9q8n{fhj>s#r{@U5KA$`uIz#R$ONAk@N=V=e${P?TB$VAQiM%I2+Uk{9OR(u&4 zOZndCWR%J8bfyquP){?k(uA)v@DUTvI(q1W5e8S~;h#YE8|hruJY`*t7_9_D)z_KV z=~Jd@wkwPKkN9m6oX>aVbEd(zIwmEUq63e75O%0q_FTHnErQ&I*tZDwc86de#{=3b zOa0oFO=6*?p?+=M;VbE&hO98VhvPw4s)7GsW^)lj=$EIbr*xbjT#NfkH^I;YP>%GZ zECz`G49m~(Y5Zq7Np}vy#LsA^i7@WMxI*-_27VTmu-^ef4_Q*29p3jEXxD`8l+n^84}ML=f^Ipk zk>-J}aq=#MZK2xI8UC}~*ONVc%Ji%){lBS~LjTF;jw)6*@pZQ>*})sgwmUC&>`1)W z5sGYRkClCUA9q;!*l{_#@9cJB$&P$jr$xFqSos5YUBPZDWZxjhU6$EJ<(Tjs?yd$L zi(OhY%3H&*;~L~&AchA*He&;eun(Jr=)3|eZ8S(b19KsNh!aJUK}^DeG{M106wx=5 zG0_kvVyQ8cEJr6BTIie%USn7xGM}8!1O+*2?>Ag;0Sir8s4>YLV-R3H`+L(??-I$I}y-w`}v-w0N%;s|y=D41edh;odb>f!vEa)hzS)j!ij9e zzt7+|kRHr=ohw{|ByJ>+2S$-lag+~qXfzA-ik&FUy@bXrRan>weFBAsif?CN9x0zR z4&MB5h$0KG;6L#{ya-9cxr5F^Ew6`ngf~9=D7lED~+T zsvuZ?^ta4rQpN^XtarQ#XDb@Rogv|NloeS9^grNHEy4_H2Tr3$W4+1Izp)$FmNnX)G4~15-}d*zjnj3v7nQMnnfP<>?w575yUP z&d^xgiH%3btMQAt-kj)q#?56A`?P}j&L$Qr-rz>R&&ZFm89=@0eAZ@xMmKp*k-ZI^@7chu~X|LM5P*))wW#@Pwzd^-CqO6N|5j!YUd=sw843lo; z^Z0iceyRgcbcVV^!VDVpef+Cp4|{ddZ^G}bWC+W6jAg7cWq=GPRA?`YYMKh~pkomK z1b!>LFAc6}cD#Qv83$LiINn1h-{6WX9q-E;iA29nk`FVZSDYVxjI_R@vB85Ys#iMR zSHGwVk40yp1n+A)--zgg4Bf%L@rp-9XA;{vS5b*a?`7z>niMuRdOYF3-KMZ{(Z`tb z(FYYaVQ|H|YRCJwE+GL7b^ea7^9*39^uO!!W=6ls98Z2lyyB(NJBU61glfZbC%l@ToMF?E-(`jZ zwyuC^0a5h#N8k@Xi59q#A>q+LhSjDfCMt<9gMT=43<)p5|6%L__c>(Nbd}H0UB`I# zNl-3weGUN;w$WcVFe86x<5-Z{mb(z#GKAK zzmikfMrxn(&OrZ!iraGvCoujR??n~=W)6mcy`96az`c$gz_KG8g3mE%xPpwwPV++-zcaiDsd4*T7KQHz|s(Z@|zstlcylYiWmKX9+!n?%d ztII21UHA%Haj8L@3l}kOg+bRBPG+h~FHd#t2Dfk%@hWem!f$d5X_e5cRwajulWyU7 zQmawfL&Xocg~u|!*5F@t3wJPooxyjwh4-?!l?H#@EqsIdR~dYdiV_zVkd@`$rrXR|)dUbRBkI}v)=dy4#MC&DSo zAu#v|K1Xk1svgH@6~pvnbmDPQR`PO>Wjc`u@ZYb=2xlap9=CvxHzo}uhlKeAjpXcg zWsUg{75+H>M_$7}&Mlc2jUXNghc7~&a2SOWJANWKAD0ZcJKW_PgD#Grd&#I4@T4rw zEg#*+1VdHYr6`uO?f4bU`8BlA9j$UQ7x^al5}jO=E_Y6t!UmogQlwmGGW*FnhX38a zfU5*=3P0-LxQBMq48>9y8#u&cYGa{bQtMT%JyED%+uByu&@idGe(j`^5+2h}T2)gs zsj8)M;@YOQbrb8Vs_Q3BEh#N24eTArrb8)9wA8_lX~}A|+Nt0zs;$}&^I~hX&sU8_ zFJpBYR#-*4p~o`nC0K@Ge`}w04zK(TIq(73i-v z6~EG{XgIC3s-k)wd=*#LtOj1avbFvbh*j3r+UgtYOzag^4eL}UCJt<$RkYN#u4`xm zms-)Za%F2B3Tv%jwYI83XM?|DB|dL;DynL0or)`4>Q%`#P3s^9s#v8+HdeQ`Kp(57 zRVvm}I>V`0TerRqwSukLwx%@|4OOj3U)NaIvZ`)v%~eiC%@u8E3X-Y1FFb$FxfP4g zp0jN6lKB^$U9lA5^-a~SXw*6L7oN>_s}iJuRdsE(=z@y6mKG#wYpiIj2PvInZ6hXA z6DqtC^P-`i#jaf2>a1*Ns)Cn^udJ=QD(xm;B&ljyh4QLeYl1$597**f`lbT(bsQ(Pm5tQ+R6~8pbxP zZUV37E9oe~4MwsGLxetA+k`}z4b@gF{j?O{r68DRdK{b9R(;Jot*fozNr7sQ8aGZ5%&1z9GFo(1 zT@?m1cyvrAxe%FYbizgsA7_O1DAcTLX~7~;QG?M~)zpIc`qqk?^;Nn9(wbY-P*=4U zjIVjl*0YnAO+=VDiA$FvmRt2h7N6dY@Jk-j_$?yi^cW{`8-d&HT@HGby0CtO@ss*}9)9NG z_|Xj=gu6r;X&mGDyU#!q?7TcQwO^Qq?qU2eSDF=qf5UKAn~fAPfppnO5my}JIAtiT zFDc?Zm@hm56esl)T%aA^_X)Z@1v>Y^pO{C|^sihs7Nt@d)zdzLe&kFF+>wwXV%U_6 z<3FW{m=?a{{5ToEIDT9+7(WS69OLEnUQ**|A*20nuBdG?FDl_~?e~y+&Umv5=h|ox zW4k>p=X{>~)DpwI6lvsmrU$O_19F^+b8R8yvrc%4fvXMNWZ=yPsvSMjJ!J528Th1u zKQi!T1G^3UwSjo^sB+Y<3UtOAoLfDXUu|HsfuAz)76b1#@F4?t8i;MO%Kw6a-3I=~ zz`q(8^>n(C2A*l)M-5zNV2y#78+gBgUor6C4EzrRUo|iv)1B?$VKU)q2F^8*7V{Zi zY2aD|uQqUpflnIva|7Qr@XrQDu-=f)u?DJxL*RQ2{+|Z!Gw_cF;`(Kkuh77;22M3_ zwtX|6tw^b8yF`Lb&TQMB>Mu&EVK`%Pn$7As~=S6U32!r zbMz4kh{3tIdjr++-4Dp7;x((+_Hp=}Ww;Y=pV<8~XG7*$qr|CvdeK6b)A)P zJKf#4bfSKFp~TPsJI7hkcOKPs4{#mbC6{*h5e_cN|iA`{~W`b^t)ixf%;4q?|>F@x_~* zSfThl{Lke&-h+74l;GXWA_MW}H<)>}Dhbjo^3Z{JGyHv&rp22|I2t;MJV1Upd>ofu zCEoPDgd|41`2xHYZ=MU7Cf?Na!_k)+b*jc9QHo;(%a2k}Dp>a7%_iiccym3P$l}f2RO7*5`EI&GlbvR99vFHVio363p(TCCJ-VBY6hz?=O(=|3K`ZnXv z(3lZ#{v5?9@#d3^^TnGVXTCtZsbsBzc=L-au@CX)bkI}cP1Uo$c=H?*zED>o9DR_5 z`r=L2<>17dwHR88Hw#%IU%aVge5*_uIC%aHQiUgyc+(m1D)A=eVVT97CZ85>Y9tbU zh}HJRo7AQ3NxaDtyszneBcg*?F<-p-kId(bHy>t7U%aWLTfTVnc;-6@@un_sX7pcK zPhY(GSC-I&c$2$P?`fTHVe}fNd`4sC(GyrtU%a`GgXD`hy){f~#GA}ui8qO&zbSt3 z#-IhM;>{qj5^tKs*@!ny(m=fFlR{}(i#P8;wUl`C0XA>CMVn1kEp;#l&ZtyfH0T48)taF!3xCn<%`Fi39N_cmq6QCU!>Qx0yH)Z=TG=fp~Kr z6E84{=N8s6HV|)K$T11Tn{PAm`6h9BVGbz=;>|854#b;$GHvnZ5H{EnZ~haBS>jD4 z3ADtUCo%p&@n#XpTH?)rLoy}aR0}{L-lSq9#GAVJ!-eyR_9EWQMZ=YNlX6Z=ys35* zfq3&lR;p+5ruREG(h_f~&A^mji{1mBK;>~faw?l_m~0CWrSgCz4_wB0>P%Q8p3sSqyi?xN8=mUhQ8 zxx4rTeh}%NqLA{@^^U7J1$V^>$D{2ASu(nmQeea=Vc&0lct!t*|Me)*!sOO`IX;KJn}zo@FZrnYY7s@3(Et!Zdn+thq{OKaP@ zE3RCB)h9SCIO0tC(u(^%aUA)%gTuFCw0s#X!?oC+Tw81vIkTo zkHB;?Gkt+`GKs55l`96~*TWXOENP3u7kV3m9qqMi3x5DQsS-Fj3$ zqc~P1R21X&Dv3QTNt&6+N}AiJ9M_hL9kfhWWNf-!M~!H=MalS%kbGCG17soPJ(AL0 zEeAg0B3<=>!>X&1>-HkbJyaR2=wI|8gYB2QLX=;kR3A#JTm5^6tE#J8>aMUaBG%uy z1Q!$Q?_64%bk-nzWlL2fK7a`Vn%7mYMbLY6tFir(kXJ{#{55=>eiu<*#dj3b;YDSnc5URN8SH{E~+>eu&68 zJ;q7g&A@GXp9MXhPqThQ@DqApgr9jh&UC*5y{Dv+z!}Himkk6^?(2}N+WjKsZbvM5 z-e!}``6be2Bc~1HT>DAM1Ss?fWy}~|3HdAKUJ%!+^LbX4?P6OMuMJ4IcZB0i)?W2r z=vhGrgufedTDyG8VL3{c`l%m=K?BuE})X<0xEefppxeT zDtRuTlIH>{c`l%m=K?BuE})X<0xEefpgO_${!~@EHQ1V#^@?1bA z&jnQSTtFqy1yu4}Kqb!wJQfc<=_q+FppxeTDtRuTlIH^c(WF!IT;NKc3#jC|fJ&YV zsN}hTN}da-TxY^J8hD?9 zUn2zVIG-b==YHRV(vn&?fKM>ES}%aB^#=b0%6ijKRo!|p^4%#@r<7(Z-+f!Z#C-WK zEh;*&qKH|!y+@0Q8RW1fZYIHFPU?r0&aG@Qei$qCNpR9p2fqsN9%r0L5;JqQrV4ZFW*=&3FRe{@{P-Ai)TJMMS}x#gO1 z@RX&WBYQ~Zs6kT)Q#h4VZ4OOz&%-ouT~?zfi#v=1%VB!y4*1sMM`8e(HSC#1+~FNT zaGupz+Ve^c+}8#7JqNGu?W-s&{po%9eCoK~(3Rq6PbR$1lq&kN(L z3|ws>Ckn%P#zB5c=_&xOGPvTe&@IAW2_GepEoQ!kng)Ia<+D_M+Vm)$dRn&pRFV%h zrW)5$aK1P2W!JanE-*= zNNqr5R8J;7`n2KkT}&Lz4LHr3j#GYYA^_b_;h)&>;FrV$v<;6N@r=NRM=1cb+x8+N z*Vyn#fRxzq$QhIS9iTb!S)h{3O>j;RHav8$@GKA#CAXa7=ud&>_%=L#L2S%YR;U;@ zJgy|FZFua$ijs2@>kS(oRIdta8y=Uax+ohS|A(562#=4BW~^<)BWx^lgrkpvpkR^c zXy!F>`O&Yiy#`ZCZsBZfcoc)i*zn}&sm$%$@EFc2OfAywi1KJ6T&l51^mWEf(^!7= zudKsq8jD3=AvRrO!=t}uZG9Ua6~ufS9zPLvtZaCMZ5tlphfLk5|lG}eX&R25#7~@iu+`=g~Ji;k9Ji@*W4{DW!eI>Ui zSkDwCx3F)+Bg(j^b^8`Zsp1&+ZFtaXL)cexQ>v`bKY@06#jxSA3>6h6H)gk#+=v<_ zx9|YaPgQaY62CV#Jf1?u@uB{6h5OmnfenwX>RfD7o!nY@p=ECo^o>@L0z-SW0fRR};3B+^!)tOUbR4_<>4p%_M6nxqSg-^24y< zp_bM_$?X%YO<=?0d}ixK$&IYi;4o}>Ji~;64UZp@OrYd;IxF3?l3Q5m3>zi4Fl=~S z&Gdl{54JERfs$JjV*@3(&#>Bo4G%DDPB>6< z8_m89l-wR5DND(1D`{CuZXaX3Wy9mYnB7ux8%*3%a(jljrR4S$OShEVULf9!l3Tb7 z39?mk3&V!T|FFV=lG|{ieJHt|haysx+%Q#-1kfo8>_K!bR^o^waS*4TB&N z7hvkYGC?^S_=gCa`Awk1e+BOeTM!)<pFwa@V|3^mcjeCVd{7T-~2RXdlN7}lG zY=6|@q}m86<04{P1am`7j=<}i9D$!E8(?w-KHuaBh8IjH1`QLc)J@DwoQnf*7k$qs z-#+8y-b@@v6}oN<2S;voN5QYMJ5OUgIUChC`Kr&EK8ROEF-Xk{<-xxS`Hgiy6J=-j!OeuC^U5dnlR7?1)vm5 zBB|yPsK7=O8v#KcyV6ENU2R-jw$Qc?w8aZ1SB5yC)CW^1DfTlejFAknuHl~z+aJop z$cHkZV2o90V;F%!7O}S>hFK0Uw32EBgJw>sfk&$ln19O1hM2NQGsxi^NzwKnXj=n0 zl?jg2qvNKUDs>b9)jwrW#8~+d^DU}XDLE>X@fU3cMcZ)6a8Ql1v>6p;e?n}{r0D61 z4jzl8F_|<=EMka8YRz`TZUs?gG{ab*Q5uQt4_`&mSL);(qp^>`Ow55sXp}`2zj=BB zC@V9%P0C0Ji_m3i&CWna(zhpKY+_*UStd&Y6CLTcHY`OpZGc3qZzu~PSE5wb8xu^~ z;HfZ%eY9#CYoqMjh~*h$4@TGB*HcsdLOUPY2uq5Q9^ZBiE&i}yRfmcOT#AaU)Z90_ zBBcW=Q#C1uU$lW7F=wM~&H!UIlVzM3B)PRGOb=^7sp(F>&NqHn3;X zZ-->@c_meCZ7ucH>)N0`m%Td?N%5LWk$v#JBu?HoJb%Ya$#-oyT;w?--)GFvbl^b_ z(1WAo(soQscPf5NkC&)&J*DGDK=Lpl(`93atsj)+0noIoJ;^w{65Dk0k{a}-{u4D*E>25~}9aDjGs-!c-r1eRxY6bK$D z6X8q^94&RHNY1Xw5w^?2xGH%@Uve1CJ0*t!9M61!r3QZ3z}W`!S!Vnt2Cg=6lYuuIc(;KM z8Tc&&pEU4C2EJ@yw}HPlklQ$xqx8}M#~NJer2(%txYA1l-fw3__gBB25#0}eZ+1p_ z;~SqB-pELJf`PnAhxi!=D!U@UFEDtmfh`8!VBqb9So}X{;5{b%F$3H9{2`w2aN7=v z$WH}wU9;q<6)i@R+Ls)4Y9jlae)+W}+yQb_*c<7h3^iST+NTKLQdSPnX-Hgi?s z5+(??pz1{E;E&9LGbjJRf#(bQ*eZxjKaldzmALuENe4itW1O|C;wfZ%JCq?M>nz0_ zl=#`zQ9^J4qR_)X=2L^pl6l$ys17jz^>Lgzh2kK*>w1h-RQv$wGG^M(b2f_$8&CRv z{#k4ZxDxZ7hX=yn$DL$w*`bsJw;j{cor)jx_mwVzG?E8ihxG2I$2f_b4P5+;dILW` zt>VO+zMp>)?f4TCu^n8>*lo4%ia78@(+7eb*|oknjV8a|GM zjgx*(-_O4YdgL2eKUCGHm%g8;eWNjk7J<|+eLw%2RlnZ$^PdO3cm{eK5eBx$cW+wt z>uo>36ZER2Qpim&fA<*(pxm+8pUn^iP_jG>KS(r7)cHu4&3?WS=^m9#7Cq9%koxW} zG%AKb`@1{#JKKtKefJOsVXTh@^|OMMSL#jV-Pq5AjkTOzI_uAw6SyO>e`nv&ajoF~ z-Ci?PoRQP01$-8XMdKPQ;VtU@I|_*ve~fYBeC+XiF+_@frbD>e zH(;XSs7T?eKk!c=`%~{5VD0~)?o>-;{XNSo!%zsOYN-NR|8iU6`HpQL-rcbsa68}* zz#V`)0e1rK0^9}oDBz=jkH?MU?9sT#My`%T7+13dybA+@`NMnV;^=cPmPU$sH z@1T~dvBh_9n1Al`Sk+_)#obiK>(wGiB-g7cjg^DGUQOpZsO#0JuGh!)YUdz{u|LQc zUmw@2sqYufZYrWL9y8dXUavL;g&y4XYQvEuxL$1pUiaBuuSWH+Uawc%gb~hgy&83Z zEc=7s=aLhsW}Zo^eW+$qp;g%*{1r;@eAUbkld!Lv$xUfse~@+Y4oWq%3N!D(>(w4+ z8LNsAM_t{_*Q@nxf6(Mhy^;y^X? zLC(5BH8bscH9ncP{lV>|WvOOvAu&re^J-GFR5K?q{y^1C#jaSYnO{e;BXGUimr+|~ zfAD@*J5bF$$m`YaU?VL1gGcasHDwwyP|f@u0HEo24^S!!WjnAssALIt= z9lKtw8pIE{UX5R%yW3C&T(5>6bY|k0a<$qhSQ8woQZ2&!MO~~`&OECy%iLm@>B%i2 z4-yusgyqiYA~wobm@MU3ebBE|YpBtC4t2CD`iMk`?Eo>rrJD$N*l2Q1t6JxN96ULXQIvMT1OKoZY zU25;>U1~BV{qu3H6?lkus=W`a?FEMRa;_(w$^#F1zeoWt2I-X}r9(WznYp?>uJ3B4i+lb(sQJkdXd8ifaJdSpZL*<7f zoM(&Dj&xba5}fJJ5F$t?-gH^VYv?Gpi}VPEUbU46UUi2W+a`|b=q|<2uHT!W7e^TB z^(O0h91Oxlq!B0eOP6)zgBOtck*ARQrRy%fj>xzW0~C52;kWBI9Q1e=%lh>u>llc` z_)NC>?z3pa>pVL&J6T5Et-Dz6huF1Y{~USQ2m76l{He%3=qImAd@P_i&KKcNp`QRr$XHyz8La8T zC)kRM3z3EQqp?l>dW*)M9-gV47+UIY?J@8L1G^1WAJief|9&*xPyP3!>3(>>-j6l_ zFCWVNzgr|wNd z`keT+NPkWz(!PW|FZ~|(syT_x-QoD=?zeEKT1Gv{4EFkbGb<=CCspy0dXPpQHv_$$ zYhSSz$!zB@T?aB%Lo%9r-M#n4CSlG$^ zcLtAH%cSmS@ZY^eT?PW5s-Anvz_sfd9rq$;aF^!&Gl52yMOz&AQWoOfj(`6_kMjWk zADPdO^$zdwJ|V`do6m$g{L=LB9j_V!^X={{j=N_3)bUfHRCw;{iFK!)`wxkUb4UU!qm%cx4Ks&Y}5!*2UKWRt${&*HvfER=q zpwLU-ACJO2g6$$bLZMe}<$>3sZUEaRj_K&q_s3&FPx={E_35SS0@9u{$4H#iuL%KQ z6?|7})h{2RQor>5G0j88{aitNHzLfYH`A(LZ~NnGK#y9~;^^h?J_9A329#z9V#PfS zKS(r7)DuXT&HnfoNSDq2I3JCQqe6Y@0#3jnOq5Dk^|OMMmu(aGZtRaa*8*pk{?6=& zG3>tM{Nz4Z9Px1m;abA|vAk5V8Lp^MMr14M`J%It#->{T?Z@}#s3F?!^P%VqjzNCv z;Ux^5ZXln3hM#TVLIal=c#(ls2Cg>nN&`P@VA8;^5MpikR|B6i;omSg*8zT;#Qy1> z4{xq&X$v^kOCO@YUN-xewR*P!8ok0+hbhGfV3&u;C$ zddJpoCwY6fQ=aVlG>`VmukBtj?A~t0<2&Uj#~GP}vX+8pcmE4~`}vx0XZbU%bpNw| zKc2V?y1V0%2YO#n-UZqn>rJ?-^Av42(#3tt+%M!io4YSVe0lliPL{%xxzkA`r!p-dmbWFUO=awxN|cLmtc7{3r{p2+cw%d} zS9U{t1av}ij2Ys?{AH9k3+ctKy&Q9!}?hRwNc2}#tToQL&P@r+E|(F*iM?9===wV$J}>w;LCQuXK&(id2^HV>A8(fn;A2%) zbFf6&Rh?fDN9~UbDyrs-+>8xgBd8=6>YyfP<otK2_Oc5Iay>4r*x>58Fs7^%erYcLd;DTLoEx48 z5NiB5LUO{NVfc3u;6}o4pwQ5br!oC(Ci(ItdiUX9=<7@!rkYM@n@aE-hJHhN$@hi6 z#{$%>3jL6wIpNpve*nqnzJ_djl}#6Q=G(p>9*(fbG@L;)us$~2GsoS{nxy?_MnBOf8MXb?Nb zeS~@3#e=Jm>YK`c!C+hmx>KcDhK^>=A-we04Kd{umoYQ(!<>+hegRK=&i76qX-5pV`hN=9b6qB{)kJbW!Yl(kN5ZmkpiHWcS^L|3k^= zy7z9Omv4M`>rM3jl&SCLG#d6AiVee)k6*$60CCHQ{|$gluXD`6Faq$7La(!+fvhB_ zxEbDo%uW2*O8kSR%n=31z{%kLiy9U8I5hfSSgnz&$^XJ8ACJ-eGRZlkeuRHH#j4t_ zzBk7if!s&v=tq5Qa+tQ~j=1%>TE*0vqok@feUK!^<1h#~U&0-z#!g&an{ES8bPM_l z2{28*<+|fb&Oi61(4RlGAXKn2)L0UVJvu3I-p0^>IVGV7uDR~P(7@j}GF?7L#(DIeZP<#P|PcDj|gQCmNUow9=AXiE*>bCwC`B`y%9On&=-Rj=pT_4&M zR(G{9j^R7dh`EEu&K>-AWbWWU;ltXSz0ZU`9lj~&#sN3vULV;s5NS@rPqo!4!i+i( z-iiu#Tt!1uRW1J2R5jEg38s*r5b2DQHw#hDZQd*#!;fm);@0AF!8JH*qIElfYEq?% zse?Zo>)|v^T}VjNc{KEub`odXjB9Pfm$|idPAxpTUFrtdsw?U$*0t8JT_uh6Pw!M! zbq63zD|)4Fs#8&3)wX(}39V~`-Lj@eW%ml!;n2KQ^|fqk8x&cfq}NKH$~Z+=Eaec6 z6=#BV^|cOu$fr7nS=b!f=~78*>#jhY`Tln;Zy(jwXR#G?{30gPr+Pa$Ak-fTn>v&- zLH*h$>)a9nVvG#l8kS)ZXZk7?rs$+p9-qFW^|~TXYjZ9BlgcK{H{X-Fd}S63o{?ud8aOFQiCigIAE8y zVoIt0LHipWbS>)n{yYy=SAUkk0Nv!0Qv1TsL%H+?X5Us}BA9DqK+`FyX=-e&I}A;t z%x9%3%a2)a($7j?!~6ATh&u*9uIyaA$hdOdBaZi~a%15M7Pi2DDq@*l(v9mO9nU({ zn?q5<84S{q(G(}%*$5Cn6Yf{Gf<7`bfkGXenOjZwMe&u9^<4*jlk`8)S}!t!dNag zIexjP!vq1i&Pao>%`RGG&ndQcLXa3fnYB8Qx=c*}Grz;^*0Fy$ zg9F$1T{ax0z=d!+YdhW}YP%358TOpuxSWNb@OC`gU2J7w{KwPq$~2sN8gWeDAP6Aw zmNfhmY4|m1`1NV{EonG+n&L?REEtJTZVE@$)*F6KNrGxz{)@MSyiN<) zqfE>yVrm+jnV_cmDh0q6HZ>#hO2r|0{Hb^tIXQ8mW{9AhA#3E~L@ryF>-feQ^>V&FvvRvEb3z*Yk}$4KvH z1Gy#>-)i7P20mgS*G|SiZQzRr{>H#R7#K$XG2Ji&;|7)(IMcud1}-;nrGae*s-qXs z*x8HW&NUR|l{>^wV*jC>;T%uAjPd9xT+KV+ zr3P2?82uK&;cJe$~`zQ;h+KzV)j%tU)1oVXzLAxKXY%kmu{L`{pK= zbZ(FDZvV^v{TERmis9Sid)syR6Mp#iqP-p0%uIG&H!j(+Ke4g%p8-S8;ts4Z1;{rP zatW0W)^R{{V<)-?a)l*b{|j9z59jZs?K;UW!kCdKy{^vs%e%Hue$n+#FZQ-i-s8rM z>`2nIn>4>e8kE&_|B$4ghIfEDkZEi$dOl;_wA?AlZ+`Rad%=DPny%B_33^i@uX+Rh z&=FyO#7uw0o>c7~2Kmso#NLijqP@dAJ=96KEun2#B+!Q6P2P+4FX^J5niFq-1buM- z_V}J{+Y@_|!^$_HUzfFSi|;|67m@#|u5F1u9oyqCwrA|eM6v++VyrXjQUE?TkNNdD z>{0b$ACm^#k}&;rm+7B8$c1u|M~y>bPrDw+)cn+C3&wm;``x2;SQtHqA@&taDdj_chB1ETTJ+(z#2vana2Ff;HBZL73~BHP}VTU?L5XX+3`eO`sUljV*= z1^KSzdz0@_&JSld=4RrRcGed#=9L4FLoE#R#XEgt0p|PA?L~WB&WG*s73#T)#jYI| zi*3SExESLM-N)Ux48xcfpnvkzb2S6{L z_Vfkzm`Ye4t@bC;odx&!}=8W6E+pHJ6^>f2HxNGtg>X{ECe;D(@ z+s!uaZHK*>cDBvkeT!Nn@|_i3sQ=3{ueQg_(PxXy+R_erPjVysmvlHbd7#5*(sc^D zLga#6|wPPk}-ir3F=-iHZTD&jW0a^7$az~-M z$B^}r{K(@RFGOF)(YFUMrqdW5D)$38af- z3}Ve4ZhSH7Qq#dTUq2^v^jPsuMWh{p@$%~vhkc(!dApa`*ghCPcU(cI*Pe9U_GE!- zQ({(oruNu0FsALa7ZgfVw}%pI`=rs#X9sgAPQ8IhvSa7u=Q?)b7hjF%2G7ulvTHj> zIGZ~MqR+Uta_l_p485|s9{S0}`jPkfkk`)s_c$N$b9ZlS-?n>q#~93)?TO}&3FEG7 z5AD9LeFEmn_E=d5?x<(V1weMe%L_3#I?vA;R_i*|3nAgr3 zO0JE5iZ6$LMqKMEM3CmIph-Q5T%;YvHDNdUA#pa|dn?qlLs@z*;;}P)xfpqGv(m@l z*J;aB(jwgx*YDr|L((Me>yU0!BH8h8lkE$s0NcZAo?vL|SYq-q@CSS?NuLHr&)PmF-3yvHM-H zE%B0?TOrd=cz!Vd&h~zXciS?g8)*6<0l!USjG=+FoxkQYX6m%lYTLI=+c*ZCM{j*p z>$Pz1@4$2K#FunZZ-sfol^Z%w0zb7A;h`DZItBm-pSghZep})vL*dWcpI^YXKvyH# z@%rCbpPzI@&<6}hALe-j3%bA`jAXws&9{*zi9W{sdFCyoB`wV9hr@`IIUO@;Uqjk0 zNQ?MqNY|uQWp?1*u|I!U7Z?NV8kcnahVOkmM=axZm9MP}&(ot;zLDL?vkh@O(7(B5 z?Oo(c-r_S~wn(*^?>*GR9fNi`%^0uB>pS6p*eR>+9D;g)k9e8%SVpM%#*P@u`!47) z4)2m@asdY;jg$CEH`C-cZ$w|r?Yt9d7{@gHUI6{!({U4_PS%@gYmkodY_H>7*oApj z!1>PiUageXZhH?)9cz#*yx?FsGT1(rQB&{d^W8!zlSPw@kvgFepZx~cnvbF{pX&h?%9{}1=(I-l#F0Xp{}tV!`{ z<#-|cWu#X9uiWLKp9`tF@Y-^JSe-HyRn3-V*vcke2C zspHzj_27A~?|!uCDa@4%x^V9Ea-{up-M3<$*nwwY$IzELZsW7R+iTB9S*~+yXFhQB z*)tpBT=(w4`yJ0J<`ejY0=^3vzXR(o-hVGMKi6iJF7XuLb$Ca-1fF3O)@LU#A8RD% zI&S0WS^$`j_HJa^u{%3hKky0o>9$iRs!WZ+1zlrM&mI2zMDtGV=o`24hEDc3`x<@Q z#XjCuw5J1W#LJq$+yi>at_>(d)f;J|tgm^-)x0@dKi{`IS9Fa-JNcYR_=EpPeI%vZ z^$7L=-`HNc=f2-!9)wZvE$D;)#52Y8dr_C4Ae`gL{Ean@e zyj_@s1q}=bo&9Jd^P$aM?u29)$Di{dpK}6b>_Xfpk(TA=nsPBeq0@zVB;}fP%}B?& zwI8%Nzc2^7T=cillsUE%>GF`y8;>9OdX~X7E^y|P`M3h?b-CKtE@?^)y{;J!+ zeJP*41p$tIN_$tC^)8QBZsz)<+P(?tn0^q-xfcFyV_pSs`6}kx;*LR}Jz#f|^B~Vf zn`XzUKZy59nET@LE-!u^@sUyNNq&iEf7=v>V@`v=0?(8uYa&j+3S7Jcv6n#qFUI~x znT#aJxu3ZWeAO`#r? z>{@{F+g57E2tI8;VX@rCO_SJf!I)Kxfzz(8srE zq=R*X{UvmEVGivwb7=d}@43%lF7P?|KIoDk_&+?yIpvk@tZ#0d`j*L#m3+1nb?y5< zf;9-?WxK&sA^a?}COOc<3^#A?ZbF?%n`Nl;8S0BB+6$PL<4StF63=y9fPT0EcR2(jLCyOSM#75 zdGbvj@?4x7QYX$e=J^McN9Im5af~VB8dcmf?6tvTq95$JLpsl+-1NC}6T)S#To3=F zvoTlDpDA+%>(hc5#(2si{XAoyczCAuTv_bTm6MR4bA{t~6UI-`F>?j=9m(}j(J4)( zgLf0=il&1z8a3C}XP6ToW>{hq=ER(oIdNtJIB3h9cmwIOnG?^WzPF$}H790Gd|mCo z%CPpK{%$kYkPXR>w~=P2gY&F`F|OqWLt?wZgFZL(TI{3vjzr&g4T(FQZ(&~Ehp=ts z&$+Sqb$Dkk>KIhX_y2Pp#TehBVcYQDe$|a7o(kh|qrKk0Y{VWs7thE9KFJDKkT60c-N`&aTeH- z51QdEo4YxGImaV>51IYZFOhE_+k$TkKKYCDI%kw^?ktOK=`MB9XU?X5!@aHGZ`-@J zaQ(vapMf_*Gq2Ba z64w{ThEY#`QP=P>TMJ`TwsyPm*~5m9*^JlBlCI(C+u`13s(9uNN4isx?vz;a{^7&; z?eIT#ljgYi=I%*`E_o5wuQc)L&atS+D2(&RhHM@d8+~u#?=i>Dxdw9}k?dkV&gpRC z)7>MH*B!OF^UqVBSM%vn!2J`FkKjD{kw0zQ-1+E~C%&;ie%*HPavjg+-`hPQzWJLI z$~Hfk&wMixKkUcIx2Ox_-gN`Yh?FNgJ~0Vv*GJ5Ij%Ns1zaT5cdHpiAc11BpyRg2U z4H~VhYSp5(mbO;D|Norl%-jUT<+X3W_xpY4 zH+TN$Im>hQ{hs@5I7HqK@}2dV<`cx6ar_tbMEYT#mLV_uC*0cp8p>totQXpF)cEcX z*j{1BdO{vBiyZ)dYCHn{9(b@`^r0E;1|i9Yd`F* z2lU;K^H5Igzj?O9zIr;!i1mhboAvLg)IXdb-&Y!o{6>0)s(!fOa0BxE1LTPv$NH%E zQrDr49Th#+b-U=kOQ^Lii+&Mi8~hr+x*X>m#+>**<|pgeg1;a8lV?`z;iFx)w6p#_ zbphsr_ikzbbo!R|xv;}r>)j}Ze~|U+qQms#rQZPd1rZL$hFwp?o@Vs#59sipgjrPc z)d%mIu4#Xx7c3T=b-=f2=(V8 zD)pkY@0Q+pqS&VKc(^Y&Y(2(9ac7RLf>y^-^DU_5?UX$LxmW3<{ZYm8L_6Vgc5G;-Njnn zqa0?Kd!qgq9QLArML*dV1(6$TkRQE5h$q(FT-(qbdrmiJpTsSDzrAi3N`oUm~;8>(eKNBFU{+EoE5EZpAxtpb-VR& z&rH-O7O%xmf~&pVtQYHq*svt#cvZGFIhO!qh2?Eu&A%?NAOVdPyD;*Il^*0yw{ z6=$%}e+;`hXs*ZlO$Bn^%D8%wFL0Z}wnTFe&5{3JtpAY4GZ`M++fAqkcIairgEaYJ z?gqXc%}~etVJ-M>_NiOg_a9mcJ<%IlbkIir-Hv$)#&fJ2O50~lfO((-4XFHe8W|ps%rEN8Gy)+qVpW3I#t#h3~>qMk|sukEYH-tPIlkv-`7WTAJS0*{u z+C$eM4(!ijkmu~jzI7CB6YG)d&=(}2oOg}?W!sH+Z)p#pPc(I(ZG&!0(O%dtTG3AG z;4go|LDY-MhgKj>4`pv`Yr%f|9e9o{oOEbB_CwiUqaMA!v7qcwtA#NF{)*v`=6Jp2 z^=`nOdzio0>j`v^`bM+u3HuMCY@SUxeM|c~guC6^gt_7yNT-9dh5eY1_QDgS%; z!|M?rj&V5iI5>O42L2HTeKQrlyJjo7D|)yA`@2i17D;4u2d*Xf7x?KwXC(6(R! z&TU@8K8`-4^1v_p5{!)t4s-8`X=S>z5a$5uw+DR*@6z2F+SV4pc+S4!VU`p7!XXFR zc8!0tjb|tJ*vW^kL%y)zWnaiVO#~12O}(O!x6s@DaORP)p}o&tI1@!1cJuA~*|Avi zX4pr(TiQ8qHDfBxSRM(V9eZ{U%@686(U?aEbl-$UfieM&_3}r)J4?eV)O-Z z3CI(aV?fPqCLemRU?c2U9vj;p452+{mmTDL6{ zYvu{dk9~-V_g|pThB?dc2=rLjhSS9IL|=AzE6nKk6wugbe3$zWMqxZwS46WBL98v+ zJtMwjg?zAf;F;pVJ{H!5T=&&rUC1?HDb{_-sE4U2_eEF-a*p81Za(xh>Iuh{F@X&) z`e2_B*zoJmklxfioIkwLPP+io9k+;Q8XMX%{;RySL!Y!WFO*$iLp$??`yH5{wvp-u7Oh4eg~Fs85e=IMfGvwsEfeN)g9f zkT(g7n146D-G9%9_9Q$3JXXRh?Z-m)p-{o(LtW5^#5E&s8}YQ_$!=|%j5~Mi!+kgl zi3;7&HWuMpp?QcyYfo?Jr8eYMY$!Ale&-zKcwkwttV2I=t!;hO9tveEbIc!aUX^Vf zDntHEwF-M8-LY&_Sof>_g|5&CpkIx-y&Y;$^Td1MW(E31JG+(RcdWgC!=W8n7?%+a z&XUv_OphzDhP2Q=HeS$q&WBsRcRtoix1^xeq<-<^cIVcA8NVlU^Hq2?A)a)<5xD3} z#Q9|5p3E+xQ=ViE|Kj{P8NaV;_wNXQ|LJ(Y7-Iv^{;-xnoty6AJ`2{U@2)_*wi5PY zZm#Pt*GsH7GhoMcq78fY5oj~$S2v(7p^c+Y@dwv~3@(4Gc&?596O8=%{5(k8eU{fP5RydUz*?)0;L%8PBQ#^bCD<6{VY zqk}!R=z`UUX06(A=t;~U9vR8KzqN<4pL!U6+P1;(4m{iOu+KsNg}!^!+tH!L2c13Z z4|0yey+f?i`*F=lIyG=>@3=j9LTj+LTHQMoU^v&mUGw|M`pQoD1nWq&USRn*GKxoH4TRuv$6BTi51yZN<4_^P$+m*B)fQv<7orEIAJ0{IHGf@j=WrnYX6T z`55hy`Ivt;%Aob)?OwIdFux6R%xCj4CWSC2Iq-}AWLNCQDx~9r!+5Ryb?y=D$kKaX z=uG;tFERbcMaX0J71s)0O_aXnIWb!cWIpf6d{OmDbg%mul*`8Hhw@?1u+SI2?(VyA z4j;?%WZ$ID*9-8j3&se0?4m=Q4{(o*^8l`qPI+FJJ;yM941+OhM||60#> zvHGbqjcZ%6Uch{nZes%2D;vK-oh5F-{Dy9k&#yC$40|qkG0)HE-VEb{_mXVbosuql zdRV)pAbrO+U>^AZ|Sou8pUA;$4cinK$#G?iO9J=Hemn`}DxL@D>t7Fp#E!+E2wuxWh$gL08 zkGSW@Pu*G-HdA)qpFeZjby*$FEGw}4@xGnStn6Fjjx)2Kjym3~)dRCV*vh6lDhTbK zV-Y>Anvbmt1wQ@NleZ_XKAiTuEziIBwD--sUyChW^W2N?e)MvWq0`?=8=W?kyXu~l2^ac5%2|Ea`+a^_vFpaMQ-3`0!C&1t;gX*}bni9GD<4nKyRa>P z-{Uua_YcoZ$Upn+mAgid=(_h`pFUsQ(*3giKYDAz+$ZkYIwQMh@slr&DC>J@-l;!* z;ll~BuijaIc89;C6MnhBv-&5R?`Ow8J$Cfikz)ss?K#$b%sOU$W_=2gw@0jl)*h=F zmxH?73DG_9KQz9l--*Xn1&Dq3`TgVlxKZZ*{Easv4n9iEA!`uV98Se&ZOlh-Vj>^G z!9`B<37jtG6F9N%@2C1BZpWbii{s~YEURnBuj<5i{Hkg<^9`N^aTcXNzSF(8H4I}` zkKTyOusB8wQ(HnW{jHvFSRx~n?1aDk(?1eN+)fIAIwxvaocYSnz$oOY7S8BFQU}Qw zbC7=|18y(V)wg=GVLTX~M4}^I4T%-9I|hwJ`OJ=RNQ#iSrxM|gkHfk?8H5N$xDOkE zG?)(qCF}124cFfTN-^I98bJ*{ycP>!sA-W;38kA)2?gDe=ZAtqk+fP?MkEcEm1$C7 zStH%P^aqBrBn|QnBkP3#>LQ!6>JZVw(-dKyjq%lf919mCzVcsm5;K_plVR+Q^8Ox9 zd{I%~!)ncluiUI0MK=Sh9*Sv!73X#S0CP{z-+=IL!OC02 zSI!43im$w%wkZ=pfn|Kr5`3>m^Y{933=*b%{&FTToRoPhjd9sVT3272J8UH0AT}V5p-s73Y5f z@sEhFAh+MpJ$KU1LCv)l`4~mGpO}rUF*Na0GuhJS*{)#dJA^M$FU4 z8JDS~oWy3^znPZjG8rH}{x?W|f%r-al2+m?RnCURS3bxnoUfzc_2=85F1S(!>hm)$ zp06an@^nOm;wwK!ZSs`=LL&cT#Pc}um8w|kM#8G=DTu={guK9WcYIO3<@up_jE6WY z&pi^ij^ZnKljVK%=t+$8k0gGM_{xXq^2eG0=| z(>g^Z`9EZyKKdO+CHwJ0k>%NSm!eYRiFV8=Syr0!nf&Xe2#4nTSBC*h4N_^o7m42KM0}+~zY+13nG81~zEU-)i1^Ah+BSF^n3BYKz1cF|;wwKTFPHer0SwtC zzH%IEkxP7KG5sGezOn~n=n`L<25(AyrRo5D_EN?I*}ak2MHL~W%l2N+&|>Y&m8}ou zc_wt-yy^HqWjFqAN46;Om2WW`5%HBh80#dPPCSXxy{}>J4Yesr5G~a5QNu@dyB^E? zES=nK5#S zuN=m}T;eMqroBsiWfIfp5?}cjmf6X~SH1)Wz8D{l<}9{Rm76h^FU3utYMnY8^bv@d z?EL@(tn-j&S^J6gp2dn>X8EXL$EElTuzakNECQkX+z9#zs%l@hRU^J~5EFfghhbX2 zt@sbeqz}$qK-)dpyisXr+0DyAyx-Hxd@zh*^1L$Njep-;_+#BN_2P#J2;wV$06$)@ z_f4b`R22;E=-3;&V0XzL@27lQTZUrG|1o^qG8B8`8!of#(G13u=!dhIY25vMk@7Fb-Gi zILnzsujBc3j?{hTXrU!z;7`d*Eg@bLp_H_|nb3u)9NH zcW=YpB4*IX+8y2S8w@(tf6?w{Sp!bTqW({3Fk5%@?J%$CZD3wI8G<4vVmJ=%zm2rrSj{z60s)QjblZ3Dm3fEp??&^o2UVmg@YP%hX<`T`gd0 zMXy3$=3zBs$!|djzhWue;<3q{lG0dG$dVtpl6tGjo&g8o;Uy;ZZjVjJoL7m5yIdZA z81@j?)w0IY(GHJI$CcUx%IG0i8bm*vW%01_8<+>bm(u+Qx-95-TiE~mF8`uesr$3` zzhKCI7UMrWo8iM6a1(e@aIhWYC?UBVfEjW0IgT!&#C^2(IVx`yr!I~vtp(PAm*M*v zmfT>+rq5|?3Cee%gYO|O--%)01F^yIeKJ4GvFVdjJ?;BU@tt%Et;Z?fi3zmUdix^g z$k|~|mEd$8v%kQx$z@-d(@fz+_p`*k<~rNORqO4GtN~lW>0PF%+_A~!y)dUT7pDr% zX%1^sr8Y;sJP5yUu?8)3Z2Id(y~>TtTz;2pzq9Fgg*Hcx8;wf|6BzEd9h?5w^x^Q0sEJ|I)qsR@tI4}qxB)t`y^-`*{5`X z3kKCULU4_?DrDaL!cim5CTmbJQv0-acHSTekP*FV70>86UStJcLac6QOx|}?Ps~{D z#At(ocP2z8$B9o}l(cpgF2I{8`eFxRn`qM8{$Fpo=a0LrH1k zCT4_C^AlGyvxS-!+%#R(9qqo{)=t%T(3$1 zBfpu0p~~8Gyt+9uYej#)j{G@Zi99lGns%i+a?#IET*PQh_nPcP>~qwBYR_Zc$@Qw% zT&Sa$rx~1Q1%8g`OksXL+(ku)QQ3%5OxNfgT~u^bfBJ~lpEsxxMg3Fk@4->w5DXZW z{rfI?@L~nx`Ti!EU80$h(JdUnRP(48W&_EfUKk{9bf8k)|`#U;q^9Bd#Sg82}%a(!Lcf4GH+vg2VbJqIk_2hk#Rfu`xy z6tX}?KX*_u0t|N1B^4O7*hN{)fspp2MlR8-aXLfi*{sB8Sx+-HRWJmRK$Ymyf-r~N zMSXFX{m@$jjv;1UnZ0jE*^E&f@>9p^k5Rf@87t8jvIUD?+RGoKRFykl#rK^k9p7XX zUqktrckf22%1|)08s9ViQxr3NP<0cUv)0>lK_MWa3Wtnf_9aCZpz00Dq<&2FR_Lt( zyKx5MWBnl&WdMEVOfXTQ$&DlBPswSVK{fyh7 z(xn)>{SPP`E^RosA28??%fegzmOUxnCi6X@kyzCQPt;R5>4Jrl5}FvV(tE+6YJ@jA zp1Ez$A5@KwTZlq7z$x*%JJ6lgRLyU$b^4;daDFj^-5hIk<;-ATPEtYhtF-n_v1$ux zQ`SJRzbTecjh=mjcg_vo!HKG@Vp;Icrj6dg-($nLN$4BBgP&wnZi;0P&Zf=Y!56Yg ze^1f&+<^_3BEP;DTdr(dz0AGutJRvb>1k&WdZe3U86VUZ6rSkSIP(J)0#-Ummgr}* zz(hYQaXIHcwb{eYbKj`wU*g|}TXGkv1-;d?T-iDQ7d9@PW7vbtd6C5UA3nKYO z{H zh&Vt>AN+YXrKTj&Y;Hv)Z6jL&*P=u3r*f2XdZ-yuPdcKiob)16_ULRF80y^-b9FlqvQi1 zl3(ZFTlgEE%)}mn4)Oa3VQz{S!5|_Dgas-60M4dXJ2eTK1K@&VlCz=RkIKc`n4AkD zc`pBo_*aF$v`g`qfWEpV5hg7(zK(yJ@t1xF{_NSw4*-TwW9Yk}b2c^GDNiZQK`Z5e z((JKPehZCVkkStDZ0eKpA<5Yz{s~Bl>JL3)Ntm6&@A}zuQ2dr=JYfXy*9Yh%(Aq-?cuZYUv!+UnEVS z`CLDn_E2py&@o%!i`6&R6Lf+8EJd#(AL%AW2Q&7O3cCR&#i2iOW2d zteX9qQ;{k=CNA^OOyq<9%xX5s%i}W7r1=GLRxk|(v7BM`ir2Z=Q}tdU!{UiDP{ zAba}W%AP&_DQZcqQ=s)stLVxt`_y1iN2?df7)*~TWpZN&0|Me*A^uM7W zv#(~{TjOkY_iTSWHpF0rvfUELYKv}XAil8)iv2e_m_exBx5w$OQo7tb;&hjL(4z;x zJLA~pCN;Atx5lx;r!lOL!Tv3Dhsj;g?2~)pFMS~X>;=gwfZ>baE@d=y&Za${l!;2y z>PcZwgXZi>IUgEyCnbb=DRltPrlge1NnVh06=8nL4azi-vPEJ4lpoP}Zpy=i!jva8 zJf|%Cq`XRUUJ75T@@(pr@@H-Pcf#xxFFH5$oACh8ri7G!3gc2vSLjR0R5&JeJg{jf z`p9#j;hT1;GoayXf2rp|!}s}8FM@_IFQ%45!#4_3YoVD&&85cb3TXPkSK9ZWDTLKL zX4G9E(tm)%p;H5u9=^Z-qY3cu>iwE!!NAh^Pn+0KvD|c@8Hl7^07cz zG+M`wDpQ?+uuB`MY^|(D#;1~*SjdbNkK9I^l;-QTr`?)fnH|*RoEET*6N9!Q&;`)GhV|rG+d-@3_OUGgmut z>G*rnO8;3}MhG%ZYEqmjIq4bs6|?b|nvpRpZ>Qr=cP>kJV$+@FfKLk7z5QMr{)_g&7S9H(23(<{9t*QqXVsl3xUX!!!WMhBhc=`E$9p!1_sbJCq| zyV9J7oIBGB(!e3tDZI@Yl#`nqtX%6f<*d!kWiWlxovL)_)O6?7id~r*!63~Lfu-qA zm-M{Q;!wzGOm|L84>|P^%aUG^24O4CCFPY)*BqxYC)4SY<6M>l3nVHwJ&%7gTAVY= zgTYW~D0OCjMLrT7%CAg=!SohqSq?%9&Tx@E(=&tl!Q9kP#Z;sxGdLrdyLPJ6Gbfne z;>4Bnzf1YZU@(7eunHLf3m4HlhnaL~I`b;X7{}$L7UZXeQbq^Y23w}5)CS8S!Z^)| zU4yjbI+y0qeah^X++bBzFfC(RS_YW6w4{cD=Z1p8O5pS$qU*$#J2P{f%yef)jx!1| zSyvgHo|~3gJ!)D8QoxKM+hAHIvLbE`bk3#a1&dR&f|bypGmDN#I?u(f$#Pa8MZpYk zjZLqfo{NZ_la`n7^hL4G%1N764QibZX0|BcOut-%RI^OLKC5?sQ8ZKJuuOkdu!(G2|m;d2j^M<-|e|4l~N1 zvoifrImSkPM2L5&#KJLB(JPo{4u{!C4PHFIg9odJKcE;Qh7Gp>A^gw zFlUKVP(Cx5mg`I`zdALP77C^Wok8i&m>lPZRpk{y=Zu_SZZMdVnHqGea*&^?87aZg zOl1}t?NsDs&@2tv*5=E}nf0oZk?zbYZ>cIt%gng=3W)d_mAfREpK)L_3^BXiVan`lVS${K}uT~iTC&CJbAnGu|x#CLCi~1 zL#d;tjZQoe_77R%+TUYofjMJ912`o%NDe}96eBaup&3NVr{OoVog>? zZeC?>b%oPI1=3L2$ssysEd-`;zce=CY zirbv7XqmgvM`Wa5Rkp<$d|OH8Ip`?zE1N^XEumn(bLlSRQZVGKUbQQiUi}E=h@Io? zUzv{X6vT5Y)29bpozvHp=G|U#)wyVq!6g^#z6sesJ-B6h<;5v8Lb>alwX4=tprp~E zu3=VPihRmx&U3n#`z~-UD@RVU2{`xRxa5;7%6&I#9_xa4sUkw3h`hPl&RLs}CY9lQ z=)J9C?b?E1#jF(7bTS^#3mpliI-jh_$!pF_omnxXG?e+X>A_G18huWSa~acto>e8x zdEHy?tY2A<=9Zb3n=w5jmAy=c^T8EsewOzba&O5MSEgi^<@&}~I(4r&J(0iVtQFPi z$X@4=b=%rjXMMw(%G8n9Iz2zSW@|NaQjno6gt{5Q_nH5K`P_pNA|7RtzUs&8{nEr*Y*Do{YqJ6(NGv!Js$kQ6w5 z({nY;G*t6*GKU8T>adta_~=Fr#<|W?REKcLdu>7)Vt{r=sY}}h*GV@lZ0nVJmi_RU}i@{s8&2D zGT#StZt6^l_b#Fr6E4sFvtW+Jw^E$ez5(5o__*urb)FW-GcO)zN8xz!EWW@+^Q`@2 z(8fQ{$hURyKc$0zo^ZR<#RF{mHSyv3bdrnac{Af}?0NRgd@wXmf=Qe7^K3U8G}6hp zWy9t?sU7DsH~x9+EYi*Em;uuZp@ddR$H`;Wh(*!e~(HLEDVSD6@~ef3SAfKY+yI zG0N=vZcEhm#@y^Baf8JjuGu=tLR_Zc>5nIXCk{^n9t)2b&(2iK`V`Ni;dm7Sk5RQN z8tSa#vSn3GR&8BFO(|s5)D$nfthCW;sw*2c(rR3~xV*Z;YN%NZ7Y!w~kYi(&!LY_^ zs9RcVK`2crM9DSQ)s|UBmz6Zt)htG~6*oa{VO=eS)D$&jjj_tW7tF0PY|}PcU<=+> zQCUL+r292pvb3%Vd0kaeTU@O~6V`G^rGxaoqN1A8s`7HWf}}wRWYj|Sipr~tD;llR zvXc5HM5=OWeTG$2iZ0!%p^a6yvpET`BVrnQj#!Z<>$~HH#ZjH7_e^l=zg@EUm7wkVg&lTvuBWP=6gVv8<}9 zk?&a*6*nr$i7Ky)iW<+Eo;MX`hZ1inV<8t6DU*`ohKA)<5#&Td(h)@tRn-tFe8`f>n^G`wcK#fQ(5yfOf$Q3GtF*Wp8IDY0W;C({ z(9UqQ-&j%0>Qz*7X%od;szMxNLF{7*qSR1@dc?G{LNR-pdJ93JULq%v)G~@rVtVRS zUS3*Uy|fI5)n&yiP!RQ_P}v$9>X5lbbMufZR+-dO<{tQ((yUn=DOx2vvbOB9#mf;O zl2BB>l#xO4GsDFPvjgnDx=>Il{GugRT?2U6mZFX{A*yIQl}L8oV#K(yva~is1F}Mk zd_qNI{-C%@t4cX%L7S?()T&i^j8X#&R7uqQqKe|0nqt+QpmWs)Rz+l8M#f01XmK%i zxR;kT>hgjZtFp4{D##ZuE3LW|NmaEI?5k8Db!^xwzf5*fUZt{HfhvdiTQwP@Ysfq^ z%UW7eR|_79c3E*PZUWTTH`JA~os^>kKy_}YE@nU|w;Cm56)^;@szp^JWSv?~I2Hyo z$(ZA-G3rJsI~T-2C0=>0L1R9FBv0iV4Hd;}HK~Zy)-fNDZdL10tbQ{yP3;n$+ zd2MAiutk*BsD7)IePxEFAk!t4RE0ve=q>8d0qKg4)>}eBvM4guaiJ|NW-nV*vb3QA zn_fmn@rOr^y*zEJgGvDH*lueudBmgQx|sK0f!MT}}uaRWL$on-bX!N!t?;--?y zV14n@>R@IlG%9V@$ce$mhLRwvaCOz84B}J-a zbhIcXRQC@{YnL{bm0D%Gmj+YSR+TKX>d{%n+L1|8339EXj@^b4)5@r#!$wv_#?~^G zPi4p;v`lcSMzJ?owac0yb`xz}HG<2KpU6DjeK+y-EL5-2S(0?r^TjpA=+lcz8|#WH z(LthsYgBb$#c7O;9;!8~7Bw1OQA1fxG5q5Fh581x>58%@M zi{ZHe3Q;w`3idDP&)5RlnKdedG7fIlE7(c6`Un;*yW8?wRcMS|S!qB;2)zKsi&k?u zC{yg%d{GFPY#?%t#b`n0wQ4_9wMlgCx)Q6dtpW8{4W~u5DpI|q&H5dgLz=mF&ld0 z2U_dtIhM81<QO53i&~AROfnNg57(Xoi!u;-n-uQvmS`B_XsS#!D9s*50>jTx;#W>Vs z28*H{pT`Bo*qU?kmy-+oZMYj~Jh^!A%|2K5Tld1Bu#x>?m0gFpVov}sC#ii1 z<{#6~JaO~G^whe7o<+H|#z>*t{C`3&tSLLTNy>fYa-MoGB0`#a9uVq!>!<@(b)+cn z13(@^qG>7jrvrIx$vmKcbPF!!4VSAn^jf2#$tHh1bbmHz9*hnFlAf#`sy!hOOmc+Q zV5?hF+%Muod$woRojjp=%FI21B%#?pTU~S+^!xBk!$beoK(psXx)F3X;`WBvF9(`E zF50gKZT7fGGra}jg14{Yl&w@64T%=zBZT7fGvrU-2Ez)m- zHhWs64}s>}?%cy7{WfT`S4H{*(0qHFds3u70h)a%(*FW&_MJ#afsff^A{|Rw_LN97 zzkNOMr<85+DzVv5qCNAQv{u@Ee^zRxY<1#o_Lhu$(#HNY@H2Zxw0A4YJ!be@S-F(b z;c*+r(6m3oMxm6pe~&jM}s!bnf) z^fcVGqT+i0=&-G>! zCZBrFuNP8!?k;oLq++#{s=9969Kq)ATwkxc(EdtT@1D3K%+V{=QYcI8m^EP zW@IhRy&*+$Cj~o20$3GrxeZVoS$bznEoZqF#*UTRSTPfPxaAZ&yq(3}2eoEas|VTF z3NJH?ii*pM7grT6#flvhdb7_M-eYX6$0h=Ib0%pS&R79Z1a#3PCD=KHKTP50& zveSK=2mMze9^yQ~1%mt0_WPqVWt;SPp+{RH^6lFGxt3Lm2_~@~`uCh@4? zr-IfnZSEEH3C0N~2=)@}BiLUsAebbWESM@763h}DBREblTX2ftG{HQ<0>MJTd4dZB z7YUXMRti=N)(bWXE)!fS*euv0c(veq!Ht493T_s>MX*(HtKdC?_X}=fKB7>%*TgL!)#F-Ff7D_iE2JM6?aHT1Ho2dmLR$)MNbk$ z*QMx%f|Y_Tg5MUrS@0ggp9=03{FUJAg6)Fu3;Hk~GCrpWa{MOE@AVKlz7p#Nx#l3f zUhrnYdjua5+$Z>=V7uT4f}aZdkvHVmPjI~8xq@>AO9U?wyi@Q;f{zM5FZiaQlC@6$ z98Z9Jub;?sNaAck&Y?-y3a%8~A-GrYcY^N;@|Fqx`MerW6&x>kK=3z$ZwvlSFgi;6 zgOC8lhwprn-$=npg1mf3^F@Mnf>#J`5L6#2nrc};5_*^5GlH)PzAboE(1!|4{(S_4 zf)fO%37#)lE_kKjcLaG8fqZ@}_)Ebz1>X}?U)X@VShN+o?<<%rc!uCPf^!8+1ses| z3f>@ir{F_^PYS*ycv#Tu)8S?bP8M7#c(q`=AkSkN&%X%%L$C|h9yC8yaDrgI;Dv%! zf|m)d6Wl8Jkl;&#za^p$|6Z_5tPZC;5$3%Gr;7P3q2~%N7xUFZuM@mg%s~;hrmag_v&={JxmqD)?(LABIH~`HUpO{~3bKVt%vWJ%W!2J|+0J z;KxMxKPq@OHq6MkRPZvvwM4kPO7I~u-zD@up$`cCvf#&J-jnMF@I8$PK7#}^#XMVZ zf#5BIcM{?L9-)6C^rJ#QCG<0b$vt&AqXo|M#yiv;9Mg3ULg2@m_H))F2U~D zh^4=wf*C~k8zs0v%&P@g3a%IYSa2{lILRkXFjsIk5&X^<`XZrAguYnl%LFeM{629C z_8TzKqW`l5rwHZ?o+nr&SS#2dcqI{hb_o4*!6yX|3ce-i?W^6-BO=_@M5ODxg0~C) zSnw&qzF5D~-DJU;f)@&2DR{l$&4PCb-Y2+SaF^gO1f#GnR^=o(N$_&PI|ctBI2`L` zx<5m3G7;%HSMYiMf{zOB6?{(cSAqwL z;QuKR@f(EAcH%t2Qo(w`m4Y`4whBHXxL@!!!M6mv;XsRgPZJy_I8tze;JJb`1@##eb-B;+-g+5E@X+kd)`eLD13B6wE z+l78W=qH6fAoO6)$5CGB9xTs9S+9Uj5;~a(I!ov=LZfJu`)NWi5PFf&O+qgddcDva zg}z1TR-tzby+`Q%LLU(N4WSPT{i)E{`cv`aUOVHTAT-YwNC$*Y6`E%XOwTx>vxVmV zBi$DWy+G(iLN^J$OlY+Zn#fOZYJRtfeXG#-3%yNfwJw^7b3vis5c`8de=0OKvsL_( zh!e4gFLa*JJhNl`D~0Bn0qLuSUN7|hLT?j#zt9JSJ|gtTLigf&2I2J~!fcGtO=z`lf<31DmIr?ZG2AP@e1C&4B&F8{160^v4UHKc3T2KB1V-^s z{xd?M(WA2hfy_}M{8KH8T{SiD9DH#ZoV2p7WGh19dC+53d}?Rv-z#kbAR%j)rG$TDg39=!FffyaVve2)K8wCmf#{>aCU9ru@oz2-Z? zQGWREf%rt5uM5Y7zX1%-`WwJL`CKr*0gSUf`H-+8Vk2J%#))j?6V>Lc(dpKNIQ6w; zS46BwkWOSalvia$s?uZi4z#i2u{;-3cOL)xD;I3f_CjYKfL2t@HSgv4v*&oPB+Ry= zSX==-(VJ*62mR8m(A-EO-lmqH?F7-4`Yw1s1lq08#um`D8%-@gUn%lN#d*8h3urpb zX5fsXz0g_(@K{CsxBrQ6V53|aKRou=d?#pmW3U*nXjH{DUujF~kAkIwa7&<(~@ytuBR$-0}mY846S z`s0zZflez^KBVxXCf+*zx^F<~)T-B^RC6k^^t*#PCOQ^h{q4Tex}u4-6=l_P&Y%7t zczp&hvo%@i>OHdb#kdolURt(zX+=@-;>8VRms;r!Wz}MWLzOx=U0sE@>0I{Z4aGGb z4CKR_w4?i;XgNXTktm5Mgx$k=P6h-If)Fhv{IvX5cY?ptbgc zSWAs4W0wQk&F^Xu^iO{5s0_c0pf`S?Rqv>9&S8|vzxgn5^V89-@c{S*s1arC*uS~??F7M2mGQ%tK$ze4(9=%{ z4;A+@Ta`}#(T)#$RKuG#+^=-!i?L%L1gmi?Dhbna8$Zl;LM+gKqOLeow_66Ncvwk+i8Dn!BTF zstJkqF{;mxn7Biyy}QInv*aV{i_guO6A|TD-BljZBpDCSsj-|6k0Yer!xc`F-QBmI zJE5_0{Q2Co1y~NCJ&=6`F`bC4$`qU^h-Hp4zd&%IAlnH2trTRPBz=$IPl(|6kYKeS z>wpJY)<@`6p;?#do_*kn=YX|K8q5SR@Wpe$Gcq#2^c?W%9tafGlKn1o`V_RdP#y>vT?awDQq)<_i zrNQ1=WNDm7&O_NvK$Zsk9g(GREu4rfjR&EnER7)Wb7X1oQ_@CWhR;78{h^`a{G(iU zUtX5R9QdLv4Sr4~B1?m>+eBn()YC3Yr_twEuX2piRGdGaagZ-~C-~1~9L~^oz5KV4 zOIVi12eccj?fU!G0{Ki$1+*-UrxA>jrJ)WR!?HBSFocLK4Rx{@k)^SQTyvghz9-`T zs&6cq><86lE&c{s3C*C7!i|!pp>j4XOJgV*pRc3f^?#p{4a?GCTs&V%md0F!M_C$w zr_Zn~jT;$8g@l1mmR-w;@)DybB1@wcZoYynjjPBqEK7reI6hC7#wY|9k)`n=Lk-K) zP#X$iSsFLcWmuL5$JvN1jbrrpHDqaAM=pDR#Q(%>KYH2Em8GE$SD(?Dl;^Lf%V#xJ z;O7@rBC<3NFtsl{K^KXTrNMRQmyo3qjq?A$OP0nZ$Q30^<2oiUB1>aFbIVA}@b)gE zts#BAm3RU}#`O-+aYUBJi_DCOERA|LjEF3aVmgk<(tumbB}?NLIzGokX#k$YalNDI zI3i16WGO39VhSG+M~a zB}-!uxw&L%1ZaP}EDa74Zdn?4z?qVzp*nzwERBEBdl!2!0b(oECg4eAY5W1PRkAb= zGTISY8m}<8h%Alk$nGSvG+tpMT(UIOI}8z78c)%CM3%F!3Ca6mWH1(iOABJ z$)btK(s-1PBeFEOIC06+c!GW-vNTfYHzG@8C><~I#G*<#kYUt&8EKa+jed-mOP0oB za&yVj@YCKUOGC{zU9vQmF*uhjjbG9K$z*BVf^7RLvNRrNgd?&vC>7yr$m~m#SsH5*Lp%Dk9L#m>8Az->R@F=-Lwzd2Uf5rK7IqZB3Tw~p&&yc$s8Ynvp3|Qf z--MVwpaBEi6c;9}ztB5;JE^i`y7Wq|NlJ^BC5Y}4tBV_$EJ50`4I zmzI`&<6rZt;&SA_@inh8cK(R;%;~vRCAnDc*y@`X?tMj0S&ibvH2lqry!eEEpiz`i z^H8jRLSrhzZq^dq;HNs#9-^7r(%yBQ*4z-r9lpq2V10Kpa?erU(hXlf$F;`d>c~~e zBE5wucLnuzR8E^CcYe)nYWHn-*BxVZ16*!B%SG@ab-|E#pw;!`@Qr2OsyCOx)y+Y3 zeKT?=n|G?!JxzT}@c66l`lh8^_wIDTxx?MauUFkvRmBdgt{wW$DlaIj`<>yth8?a& zhA$%Pi}c}(^Xi6e_?oS`QQ7e#ySkdHFPf?g%ql>Ar8w+Ue{rBFeC=0VWG>P-Z_Ry6 zbC>_C@BW&ahtCqo)nEBafQkw)y(^8n?H|6GZ|;K@*jXYEh|$8!dyV@ zcs=+87opAl`tY|2)D3XCTdt$f@!oim`%)=hMK9GCM7hJPJ{6!+qOT^)4Pm*lOADRO z8s4L2wQ{wm<1Y%!6;)M4oziBmkhAuiF94WtD#$k~VCA}Ti;U}Zv0q($HD6HBaaOUC z+uiyutoa~=vgb|m$n9nI9RglB2W!{2E!@|R)mH{ogLYpdXO^+Dn*^(_0ylgvZVAe(*E-jP0v1zeecY{3uLmAk2(1cHDb)^V{u;AH0P5T?M`I zLmnT%GC++eW49Kxo8LZH{NiC}(t8v1^uzL_!V#=>k4YqG+VQzv5TMi}I5-$%2q4{1 z-G>KtrCt&E`vC4vl-|zg4!l#h%Xk7BendSl(w1XRL0nrbd*5n&vJOrjliw7k1)~4f(MqMU>O&)I#5` zb=xy1B8uk@Zsp+Qp_>>K0D10!Yziy-<{5%fikq4D;7|hB-@;P^8uObp`0$Z>N2ou7 zVmdr1N2dAMjIO-GuMjIO-Gu4e}d_N_!b4I5|97lwCu3&)m8s;2hd8Whl?@7-CtBV&meueYE%#m4RaBK+m z#qWf%PC|vx1NlxEYSLkx?cI(ReaK2^J9OfAzi_4(@Ar?#yuyhb?|CEVQEplD5K-&+ z&h(#UM7yi)avd=8LgxgLC$#p%`14$WKW~hqPo+E@DZNo1cD%kQvGeel*7kYsR4$p_ z=v+OwX+zcZ#ds<}`C_~_#|t~#9u1&k#zXc6#7&H+QFOr#-dQ&wBRDDXSQqmDo7v6L zA7a8GwkpAnFI??`jIat6`|@2H(SfEgr>BnVe_`gno>Q>{4hy9aq}n zh4WWm42RcpYIPZ58#X^~F?)h#Ys7B{Z(naeLT7f+m+^) z<`LrvXxtIi*dTWNW(qA?r#a_mJ~Q8$cVH1#1;{*(ghm;DhBoLn|KfF$J0=Xz9U^H{ zJ2dy>*G(}U$~clrS}z) zILm&gE8HV(yVN^e;1L(PwOzfVRnU6a z&Tc(SyFFI(+uf{ii2lokhr|U?*ey1xs`^JMY1$P}x2yHkNn=tF3Kd|>;wB@`L zvNpW^A>Q#i;rm@Dm<#r_?Vf%=r+w&i<*g^o0LNf977bD+gd}55!DLd;57BL%cn-K82QC=0>KJ>{v0SWL?6PQrmg%1BEFiI|y$K zm+G8S{#6^kK`O>WzL-+p07&o*SEcG1##tm@Uy8=}CoNC1rhHZmUmo@h=Y-8?b5?0r z+Vn?7Hul0U>?-YYdWbH(!8`W`ZKaY&V)z(IW&3W3@z$br=7?(Mjsp zn{>&UqiH;9imNAL>6N-XM5NCjA$02YEz@(?LZ(S*TJH22xmV<_#tkp~_nZ~Q=OU5v5z1!_bYJI=-3{0cB` zFkR$FH2fArZ~Q=O@ebY?YDAfQz6i9N-#qX$`OG21@N0zL&94dkPBh2nkm2T6>53nw zA7Or1b>g=X`~omD%7nER25x@!uK2~n&cyE~=;?>$N5#8@e0#_!YWdtQXw07l19!d{ zFC68-qTaaUKsV43K;y3S96JDayH4Pa?op&+m4`$nz>sGV9K4tg<~!4haKmZ=3Smf? ziiGPjQd$!q5??&Wc89?57+~I@gC_`T}8rk_rAC_ z!8;SZgs-BxG%CVE7Ni>=i=P2J@IV3 zo8?!RY-3Khc<~l9aJ#3uSK!{q?K}r}U%HK4S%Ac7yC?ZTDCzW`LG)@}BNr0XWIY>A zTK5=wvfd&|Ivr@gk3VxAfjRGu@iqa#4>RsS)Guf~m?oHUM_or8qA72bM*Z)LVn63e z(o~!k?Og#nhKoaoJ-Hp@V2*0-D za#m6d83R=P@kr%VH2+KmrM|4DzG->K_q3`@A^{fS+o-r*`_*^UBP^EUv#1p&(rVI< zEW}%YR?*~$BPp(0%AA2sr<7j%7@3}xo)t*OLI9V(%Z3M%CqvZC97xS6DQj3BfUubWMPFPpGA(1c zHIds$>cgv+%|0Ot)@Kw>$)9dTdvH>n-nhJ`sdzCE@)i_U8r?Us&Zy(dgyfx(o~3U~ zL;MLPd$?{+{|8@(!i^nt7I@eRQDxzAS6;6&)pt+bbI%0ySDZVXiiiH`&WC5H&}LS} zIf>x`ZCF17db&66c0=!O!^Tet=Hzb_-Ej_Jl(Cxx1EZ&10|>Tmql~um8e|^QaTJc2 z4%HC@8L!T3kVnwMzYlYx48P;nAee~LKlu?&zHn_pKbd%_xc{7_wd$YlXn%>I?oc3F z9q!oY8D;XN1_ti(n1^r!Fk`qFK*QmF3wje*Xw`Q{#!(~6@LLYr&2O2DABQHoV|uQ` z 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

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�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

81Wqb<71CW_;{Ky2MBiI80naO763&>1l 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 E0gsN{S^xk5 diff --git a/ports/cortex_r4/gnu/example_build/libgcc.a b/ports/cortex_r4/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 zcmeFa3!EIqbuL~#J^Mgfq*<*1S(fct5JrHcon5VjAOx?3cv^zAcgu1GaBo}Eml zbd>#{Og1*uH+VT(XDFp!QmVQ&f4ps_QgYO;Rcg^rrRtu~A1j|!YNAKe!%Dqb$NOGT zipR#3cU*moQaoCp@{aD;m73@=h&Hj0k#m)r=`rFiV#;vJuDREozJe&rosY*vcL zS9c!%@t6Hd*~jPsr6zhjjlRL-0P4=;N07}U*R0e}QTAWlr__HWm3sXleK?h`DyQak z<-BXLa?ZHMJ63E{4v$s+-tj)9;c@$e%K7BE%K73`%GnoJ&Z8TZ^W-DS`8(wM$5G|H z(5alCKd+qs8db5%xhgj2UhjDOA{9HWU&WRuRP4NsDt74t6}xJ!igoN(vFk5Zv77Hx zvD?!s_NlmveePKm`x4SWu~@}^vQ5SQ^Iqkiuu-|mM&+J!hj(1?kaF!~9l{Ui`1Gi9 z?c?k7lzTYGH&CXZRkpMYclO*c*t1Nvw6t}#wfD3P_aLBov7R@%(j7Bkpu zG2Q(Z(_=G(Hq$$xTG|G#YZ>So8Xo9VEj@ihT?0369r&PC$dp>Twqr|M-{vk|$*?5# zRuWV_EJ=fv1Pv0FWSNx&%@mfT(Mp2G3rn)xN`fX0OR~aBf`$%Da+Z|@%^#LzrIiF1 zB`nF=RuWvQkR-The(UlAh9tqI^po(ih9toS_LJ~pha|ye_mi*-gd{;{@RP7(wDb;c z8R)cbu-@K52Db%++awq_MgDd~YE-DDuWNH>H?Fw3Njt&knSsIKc5wLb?9S)x^g`P@ zI}1X=BW-u@*0!PHzQLZ(ZePxDPlF#-5aES)Z5!(9>m2O1%h8?BG2GW#AgnhZRS@BY zUpL%aP%7{!2S$T}Q1A%dJmj5nbC>7#8ot-?9oO}H5q&10>w3G49i0P6(c9(Mv!fGy z*Y$<@I zVup3p(7pk96W*yzZYu}9= z_O&N(UHd?^j?G{Dki^!tXY6MG+S|ooj9mLc``UNg*FF&AU3;${y%6AcnBfABD%4ZG zMt=tbdcF3=zc;RMDCuFpgiOyf?E5?soejgic9-n5JFCQWc3Z*S1vxNAVVhD=ER*8YyI!+k?0i?Cik;k?ZKZ38_+H<=ji_rf_CYDIvn>v;&(jISW9 z<;E5il0zj^=%_|8oBM`aOlN4>($(AFHDChS6PPR?YP!xqS6e4@8umsMtjppV4INz& zZSj3K_VjhOboBSP^b^Nj;qh0l-_UYV7j6rS-w@!Nw_3g176TY0XS0LZFIYPxa(!FR zVAloPI=cFYdbVH7{d>y->mD&qy**eg&zH4a9z}6f6VxPa2 zkXbKq& zZt>Hb#*=i?mWAoywruX`XhByV+}hXHi~FWU&n6hy&M~qsXs{?W zUmU{Kr&gM<))`@FZeY{Wt#va@A2hd(6(V<%4hkE}7%|L}ZU#76(hWU6$esTC_4lgawC8%;r5XWV+h+6x`WQ9_Pdr1G3ny{?XlV`2|l zq!Q|Qr=7}F+I3Rt8Yi|cr_{Q7 zXHL4_ZAjPu`ryHHVvZNKJ&~^8k@CV+GM%F=M>Qqg9(p}2UxQSI6hOeqY^XQ>YImf|U9;iXM;d+QofspU)7En8JLIM7jt?ze;i*Ds^Du48LochBZJObKx@dfMx~%icD4op+ha zVi2#~&y+cJn%qx+XsP>)SnNEfE>&0OJ`L{CSmo}z26yHk=>)MD%JJ8_WXu`&FZNvc zCQ_`r!cr9xv9Mf~Xf?6%<+uGN)gi%X+R;*mHq`zZHZ%^Owwj~%znOI$P z*%irE8sKs2yIwgXPPIyuS37eYxAN_kr&iXKu^tO>=%!F{vYJ>UpOy~ZbRAXh z(!rqt&)p)vfv(=ALzt1MrJWodm$nZMs->&ezi(;V(9l3nJD>!3ChF{JAKu)8;&s`4 zPha;|WYuwP?d=0yH`uXl1DvN1`sIk=c^A^K=;bCY?>)ONQp2|+oPAAbV}-)~snjeS z%+GX{I8HUZcPlG(vE42v4vsn#2h*2Im-5oUhUO9F%@qc&h+kv*8GQ{RK1YusN!JA4 zu16>8!E&%3Yy+vsM))NUyy`!|JWh`xiGyLUKwdB8B@sq>lq>RD;g>w{s=q)73aiIJ zAl*|)mt+P*lu7EtVb^1qbvLtkAPLCle6J4)Qdl9dor@spCY#bsl z0YA@a%X=P;b|b=sM4s^C+Tn5a%Yzb5*DYun^7a|X#}%({o@{B1msasoxQZtHJh^`F z_QG7HZ(~1!iyB^JI2gRu9F%9BAM5cg7SDF*#=*4na4`MR7|s?FEfq6s0{H@)3aAvj zrFsSDfBW(HLlG9>La|&Jc6E(o=o3}s(L5|O!`bKjcw9RPH&#+n>&a4j9anX3s@_krZ_IJ>q7lP3gUv@mL6i& zMGpmW!8W^HkWSbjE-02Rg*+Hm`r&9|x-fl8y&_=^Wml#5IAn^=TUQpjIh` z3mPz{M!^LSL0;0xqg-j1R`{h1@Ty-xURXVXxS$f1Pcnlc$|MDG!Bf`t5IxScL0qs1 z^0sRU-k+3-E{f#SV(5%l!Ii=V3vk{cKUZNLt>A*XAIrF4F&ct;zS$}~`S)9V0{%`M zOncOE0rbE9w9`DrEI7I?XY?J2KY(%-QJv!&3XY8#dAROnoqU?&y4S7?bq6V^dmlrG6`h4! z-`6og-5Y15MZyMkZ?SYKwa2DVgBPd7I$3b?=i_J>d20vC+x{ zueuKcqUhddAS8+O)aB#p-rtL;d;5`&@`Q@%-dAwm1QPMQ5Ba~QJJ%V#u>OsmAVjGkCaM<;zia4j;2YK7I1n&>k(nJ?g zK0O>!ukJ*Bukv$EeZ9)QADq)3RlQoMR@JjO-x2B6&Ks`lY#Hk1ljtO<g0 zuS59r(b>@UoY#fT;kKe4lcaM#Rp9`Egg->(Ff?VAOV~418GlhPg=eQlpP?R0&rdlA z<9NdHl+=jBu7~(*5zcfTe8bx!@Y}>V(rGw2?v+XhJ4u1ApuDg#wh7@P8(y^*hy8$9 z;?705J;weJV^hUSb*9kBOHy!llj^z72l%8{v7K*w(`T z5BRAYg}(#-6D{Ph<9D*VeW z{=eWq+v1PLn7R;e5#-;B1X0vIUvTV+q(hys&B=mO@yyA2vsatwGovKdeTFZ9-U$A2wHq^&)Jp zA9jKc8$s9!e%OgR>>h-j=!c!8!yZD|Nq*Qo9rhT)=J{c7(_se?_BKCkz7BfZ1aqm$TxTCPAG7Ph z@t(x7o7EHC(`(n&#KAelPE;&i5ce?Mi4fieo&*FeJP+Tlk7_gn;-(iW7y20WU;kfZHB3xqZCNsDLxl*KcDqs23xVIFz% zFBTT^7@uFG;Kg4Pi0`#{%Iddx<{zg; z!kY3Quz2SGp~W-*3l`7#mn@#~uUS0fU$=P1SNRv+JAcCB8K1Ox=3i*>z9@K(2Clu- z5|}{!uLRG&%zKbH_d2-Je;9ugc;Zrt-wl5f2Z{0b!6nX=_`iU^5eJF!v@2hLL*j{3 zrT;K~4E*m|@&5_G^dH7^p2adrem+Z;{=@i1;I~`x?*YFPJc;q|1y8Ik`P;!u|6zO| z`2T6e-ws~-592=tp5vY5e+c|2coO3u2LC6Y??6`#X(>u(BY^yW9 z!%Selgh?vLkJ!7}|KXxPvhVu#{e}5(*_lQbSe2SD)h}Nj**@Bg^MVTIOUcywvE*p` z7~a)gRqv+PRacFys>izFV$X+%S?-OyLOti*GIod>%2(rzwp8 z?Rvybg=*nq^z|`fvtLoq!#L7uI4W@nmMoTTv6n_vpMHOV_mz;yYeInd8P|z}I$TKN z&P4$Az7TOP%R#&jueMu}XL%$Kyy~BDP#+3O+*$+M1 zC&aRm_-xibFCQi)5XSQYlKb_bEl%1PUUdNSK17cpu$t(4{nXVxK0SSf{x>e>BD*{aiS7SF(UEsW7?(7n|4g5M5%f zHN#~)vo}MRtTVb~-Laudelx90rtX@fXKT5Pw3Ef8I{f?&P(o-bs5w16507@(BA{-SYN+7txKkL z$@IKroZZm^FFosd33Lg^et(M|z5nqM=o0RB+T9?kq~w}_`sEFg-@RWMV&jTc*K(h| z(DxlUr;h2THAiW0xt^}BuCtamF6r!Ey1}y&n62gB<9JW9AJJzdY>E3|g){dF8L|q_ zRz$)ZzJ8B&;kX*KWr;n4@l$yXpIwh)wybk?CG&rBX@j<`V(EgmET#*y0W~39WbwyY0BKv+G3wyb-RE@_=t+B|5>dI{%^ zV#|62@}k(Xc1N^jwc@<*x5@|cp=5_2%kvMmFKt=uKP2HNHVhSRU8!}3H^%!aLvN0c z^K{{$Z3<>86+0|jRwwt)$4>N~+9>s0WSRBNn^Nn>H=!J$g6AUC83=nhR)5M^Or^5! zXf1XhJ=c8zCe-btyR$K@{B#p(XUFJW)zB?Y3j3Mf`eZsc`#ZaGRZUK&s@cs{sm5%T zT3&rW^2D33$f)M7KS#ZvD{I=Bi6xU6H&vTa>g0@*IK}5zlwXlcH9dX@WNwc+)wx}X z{aNS4?bzpyrrZ88mB^08oSNLO)VH$EiXC2fa$roQa${vUALcec(Q*h+PV zQ{I?E7oRbr_GPyx_GX+5*Ni#MSBzC5ESVV|L)@ctzMIZAoSM$oEWIteVR<@>uKsee z`GT=z=FTxktT)^d>vHq4$4v|_ z){vd!l&#~r&)JjMmu(=fa(1VPSh`@6M>i9@Ot#)N9m@l~?DGc8>F% zd&V&STYxq+1jV}Wn+udP9n7< zb}&9C7hA}^ba%vN$KXFX?Zn}C9r$b0P7MAS{0`h?a>E$9!&j=3cd>1rn>%{jSXHup zthy?l<#REFy*xL08=skdA9K?z=B3}my!3nSr5iKi7ui2AQUA^+<2?SVfR?POOcg zO=8Z9Q^uStFPO9A;K7%j@2o``(dSe{Hg?i3wgWDGI>+|8b?)uC+2}vyMK1d#`Ys-; zr>oIF(RUx8hqTqsg}bxvg?Blws&`^1-A)-bk_Rhda*)ft0Fi#lPn}ii+%jB8)tsj6 zEaGyXM_G!ci>*tbpR}%)w7ZdZB)Kn(wx01sbvoOIddFs^vj-Q>K|fYA4#pdDvubMR z=xdBPmY;r}d)E2bFA3+4t#ig=)%$ZFVjCoP^O>bC8_R@tF9>6QMd!F$?jb4vp1<9f zZ9aH#hMP?1_U_-8#oonp#7(B!#}ePd_29K}aoupeW0&sCtikz0Z4fxYje z+~byV^Fc0=Ox5ELhh`X1*I4&H?Q*X|hA-1D|CrRLdM}HYPsivCjY!adlnh_zC(e7^ zS^lYYRwKp}eVI;&f6rseM8`euQpT;-2@IqC%k{thTj6_A5>McYh83y02L5sH#pQbc zMM(u+;y7WDI*B{A2p}GRap*AvsY5X}tL4)aem-p1L#kAZis2SguBqTgi7-6lNT=Z- zUMZC>h^Z+r42w2FmdJ)zZ9%%5tN`%_G4)0$QI@0B^IXNs{j$jGyDnxY3(RBs$C;Ph3Y@jQ zb$510>s{3;_#bTI{#wgxP`4T1#vBDWFdMsM=lPt2==lccA((UQe&SBdWzxCbxi4lu zHTK1fYE9=pwc$${yycv8GI!R*=K&{O@?3dm(Cc;o_Eojv3IT6I0n%9+^m1z$074-NA1SEg7bm%axx!)tU0m& zbiMxYXUv<|{b|O*Tq$STM(8gtbZn3rDZ#FRP% zZ7}u#_E~So9PnLhc8;BgzQ^l-)4_w!?NuAI-^E_2vDPayZu7S@lFlhl=VJd0M+V`U zzt6;4FHFvAPS;jpA9YC+jN3a-++ysrUNj%#$}^wN0yE4gTc6Ip`vbRU%iGi09rfw# z{pan--d3B=E`DNP_Q$w(yryh-rLQN-w?Ccxd(L$bjxb!RG)C!-IjnPj&KBJI$HU91ke5s|p7%1$Za87m zlO|@&lQ)m@c-wz@K8f!$vn}!s+K&wDDNc*j#j=vrdnY>d-d}Y1QdW4C?59sZ$%A!=&f6B_ya~jYTGz{c@}tb# z(op)ZLLRWdxbwE%n70igjgVb-38QO=AItML?i<5-PO~Yz@O<7w|B#9A4)7BjphD(B zoD*1|gY5*52PfDGsvs2n5zIUIInl#-=HAd4{Tnzh;ARE+&rkSmJ2jfgq1j$`Mu}HF zm$utmU%)exf4(y}3(rVq;Tg#+==)iCMuPi7KUcx?6xyF*n@*woq}^20HE=Jg@GGG_rLdPv%2kh7Ed~0^P_FJHcV)*k-T?lVX9;*89yal86|f`9O&fW(Dg~P~u*GbY-TQ`JGwRcS{%n&+_VRT! z7E9O2{sUI{GSuUgknp<^zT+^$3*>}hoyXD8lY@2c!V);xl(pFz>fO`pG&sM{gA&I0 zOwI4k)HYuNASAz>K^iI){-t8^B6i$XT)n9Z1bA0~!temA<qs&wgi*J!>P$zycBD)Dlvrol$SoiB3 z-k-!Zfwk+A0>g5%9)JgaJvPEGdEiw&82rMp19gloZzY%{!YGe&rCnO#x8f5$kB{|R_%T#5e~_=_!m zVqc3tnep0T*2Gx+iNPUNsZ9#7u-czhsQ z$Ku6p^RdB@#MZ~-8H zm*Kvx1D&X?XBsZOF4Et;v-Dm`cX&6ekI}d{A~A};W+lV*1Lyfz2~&poRT-{f@xq9g zAOg!f^55_U=E(_u3Bw~FW;;yE+R8o01-43DTUQiNtVOA&E}#g*60DI=<6wzneQmy7 z59&cuEe_mPz783I-zLV9PQ$_RRW!i}{5DCt#Rx{@Q+*ACrQ_Y5b-ne9vrvP zV@Tr81#j!sxzK|wM=9IFi4YKm57-8_yakY#H1cpy`uN})_@xZ+s_%mds|WSIo$e(t zNoFv_vXX+&gwMr!sh>*O7Ai3)M6oSoa6zfBY`j7FL42?eHk>wu3)y9tFuHblX?)-f zev*GO_m<}Mq`qEnPA>e!hM*9ADmtS;r4944Py88Wch9!Y?!g|Th9c;pm8qnr(t7BC zcPR?=5Z6%I3AEqj z=&r}1yJRBG*|BeCPkG){aU=N%3ORLE843^Fgu`?R>=>_rWuT^_g9dV*2c1yRPud zxE9oB#nLTC9$TL=9quKcL|zj@#Lvj5aBvPGBymA~wke`M`#lJVqR(0*>a!EjDZ=Uz z)Mxy*Nm42Sx0pT~iKx%GM@|%dwg`GEiavV{>E23vV2q%Rsn6D#HWz+k<4}k`GrA|Q z&Ki1d#$LIo*T+z%$uvu17K5^^jjt&x5|HDX6=Or3>mVrkiVI&=Gk}mY?C&Gk4NsNYVv$ z*V>4>>rx1aqPsRm)LrEfbyqT??iz}yyP6{Eu6Yr47vHsOmqvyw?Q8{UuN{6Y&&AlM zsk=6s3J5>3VJKX88C_$kui+!pUqb`n|r+1v_9vwg&pE3Tr`Ux$C*+>~rb!@J?C%1DO$r^R5)$pZb;?E5jPYci%QAw)|T) z4%VyP%J<xgX#eZW8%)`Ua%O`&@h%i|Z|aRb=hhSJ6)(3DV$zsvBU|vHNrmT_;)32E(( zjhsJkP`|zuQNR8I@}lV1S0n1zuSV3bqY?FMq9U5DGZRt2J`z#C-hukIhP1O4q`h+8 z$sEi2m3`XQufk7k1Palwd0lAf*Y1hW4e^e;=vC^>lDhRs_aQMlFU;OZyT%)$YX*b+ zkSuv4_aV_|E240w_aWg89FfAlsR0b-Q!$;vG1?{>JAyi+Sh}FjV7e$eqZ#SA7DY(T z%l!89tWk`OtVgNmV^1K>V3&(`kXyE-ryqGu`_5HnuvzA5vo)vKes9-)8gU&$M%iNE|NhUKfry++; z3}(b?4`6PCy(?cf3AJOf<>${C#oW+uTeQVm$b;RLZH{^C4u77?Hplg-M!R$F zTs?w!*0wL)=QvnZ<&^Ev=~W8*R`6P)ozT{+753ENwd39y?_HSAy~j=DzJ@i)vtuat zJGWOSV`%Gzx6N@8{#At6VxJ84uIM`f0TfG45q(_~xs=3>hT)zDGTpsf+lB^vtOzT7xUbWW z8Sc$zDTrYB#IdfoS4DKJyZr3PST_aR7pMU1`Z&wNuon}nU&pj}6cXn4io!No?7qHKQk2{(6VA=ZIS=bwbxbrrHIUPm(t@Lw?^7sI;+xJ$# zJ(I0StmB?>PbU-G$Cf#}v&lr;*a9b=so77t-d=XZi{~N^*l|q9BaY7}xThfTF5{H~ z{@ky|G)^r`xDK%DZ}dBABVOA5&e(SBaThDAc7aX3IP4AZ7_b$wBu4WZJcq$iUxWK@ zhKEVjd}2`yRr7&Ofl<}`?T!1mkKKH!kNvy_&th^e+K}Z5yB@d}HM|zMC;e-2j=mPF zGtOu-<20mm+vo1fj$ki~lM!A!XGb>KfIS?XD$ETwWKpMv9jSEvt-$`$hHUHSxi7=F ziI0&cb$$}_iP}Bz?}2|W{I$-8Y;FDdN$#z%2mU?q?}guO*vI`6s$a!BeZ=)xhX0(F zp+-(-yT+{R7u#PomX7br^4vV;>v?_mqrUrD9^m5d!T&w>BVy{?&3T<; zxW3p^18qIA>-2Tv;wLdr^X6yH1Gu*IV~En%=#No{KSUZ{Cx-DleGXx*2;+S3F8E{h z-^*PC_a20W*%5yi7kF~;^q;uiGb`X#Yqx_FmM+t{nLENEhJV`eSi!a)Ru;@GU{`0% zGlbFtq;*DX_X2KQpuWRr(y%v4^JZz8gX*4 z#%Ig*egmG)Zmfs8hBl|Fr~S-qKCAV53i}Pv7&pPq2F}Ihn%as!OwY4cK8>U2r@~$X zc3ni*2lpCy7CIw^3R7pW>P$!7daB{Qp#d)e+U;WE;2g5oK(TZ@2W0g#4~gl*Fu@wc z*{9kG<{LVP_a|{pU~NoLX)pon!RfNpgZ2{11Fzx}jWA3=-C@g1fJq{Z^4K0CuN8h< z-hV@0SUvcB*G@MNl}{o}h%!liIPCLs&x8cRcwX5XfN3$X`uGf9y{GARdJM_A=z7@I zi{QJ7X#`@N_T^DN&nefIDWAur{B?Q^i9F%OwZlte0{aX+-{kEza32axOmMj=xbU*q zz&$2j_8Jg=Gs4+EyG(q4fZrD2N5E4yiRa^cLVIz{GyI+a|26PWTJfXsKX380!~M$Q ze*%9M8eQc54E#cipV$x_f?+lf0banAwz^0CG+-sm4 zIqba#$Xk021fq3pey@R$#MWK|jKy99`_LBLYv3umxCt}Hea*NDv=jFlSY+HTgyXr8 z9v=>WuL0CH>RtnfVcX-LSs)*K@x2%uXQAjC6X7=YF%iRPr=p9yTic(2hqN1J=eULS zek1l4IAp!l*X7f(8eoiPCO8*mn#$;5fo@a$Y0W?vyNG&@_dgDHIM(+TuAeN^h3K6TwU6^_r;YRKg$nPMtj|HeEJIlRAHk2SamI+t=QJa^lNYD6e}s2LxCV;v zlPI%?0KY|pt2kf9u?7k*;v?-L@KIcs$H)u z(-hWMc0FVSs71v%n#c^~KEI-#hjFCSaB%D`l`g2SC@*ZBZ$h}phF4?MkJDpF;y51J z+t#4z9rYWqOcx{BFcC`rLI3zk}wdXR z=B2A>aMz|$y)^fT_U1CpT+#`956;dc{i_y%^vn3uXeTmp3<{1v#nJ`GAEx7dCM4$# z+AP)}0mm94Nk`XH`CiN@0$Sm%z=2y>YYEB^+AMhAQl^l|6JA_9d?NRm$f2uk4ii$o zh5g=J!pili)BUp+?aFI3-9Opae|eDoOvu$OBOKevzEj9ct9faTTL1JO^Yp@~r8{V; z@w0w;D)K&;E4?cC<0qei#73Jl?&$Y2aKU-FJ?z;=FRB?+8~E@1X4dh_0fE8cOysVF zqV8{N?^(LRYvwXtGIuc$xmoh~7y5kovz9#01vinC)ycVvWPYPx0$-4?3oE3Fb@6Em z<8HeifN7e7ZLK}BK2;4|4 zTMBnSgEW6-WYH0MYvH%$?S;G~!YGfrlESUrNc`(C6=GJ_$?A_Z~x zGdM5xbt&Ba2;@bv3GGI~M z+&!_urVwu|Z1XKY9Md(vn$WXxPcICMVkoT_8X`ZVXb#Z}u3DFcy=EDnckh5L2j4zw z_IcQET-tZS^aSs>H(XDw)OuoEU9k}5C^FoS|N7~htSi`q!}JB~`H|g*VmKQ4zd;JeO9{SeY6kynT^Nz^~~ zdHIYzfiPPSFm2E-`4r@BN4OAJO?18C#p9nr!4JSoQ?IPmrhJ}Lt{J@QKIj$da3PT= zytsCFX}w~ff#;jNwbd*y^~zOJaJa1Z#73*)^Afx;b=}rZu1W42Zt3;7mMvYq{apjP z-@Lu*2y{@R+4?uK4ytePl9qlRFlBVm-%snH>xSCe@!gc60ngnczPE}F;<{CiaZ_Cf z&9&+Qudg#Es)OWPVUw+cbF-W|?`jr1W#?WvP{s_pEQ`je=XHQ9Fy&3DDYiIaQfgS1tMJ-J?r0$vd#` z$fk6*a@%OOY|9hb&z4tZ?kz9Re7d3{b8lH$rlS8d8E4b&Cs#amU*@z;U(8fBrL$#N z8yv^;n6kv3kmJpvoN6agJ_rA&&Z?Q|qG!hzDz|dx%<}+>TzF@B-uQ{|o-^}zW?oW* zHTgA;Q^Obp03MYej~w(~QnM1v+$+E`sbMQmLSjjJTs|q5#2cAN2n@Cq$=*}t5^ zy=xcI`!RYi-;RIx7W`F~p@o?I)3<@74xFu*CX%y ztA>?xe2iJjo9~XVyE|TWgCDT|?)Zkghc*3KF%n3^k}NJ)5_vjxB8i>Y~f8NS>cuwzP3+V{#EtP*>Yv*XhYc z=Wp%o+OTzaprdQxrsS&i?@KnMQp>Mjwxs@aOD@m3#yjPjtAM12JBHNYmaPLrx-_OR zIA03o9jaGrt{U#^+1lp`C@52Ovxi)Z4&@8b)iAB#`E?Qec$Ttr>u@_>MpPG_fBrei zMX2-XhgIcO??9WI)jb>)%FrTbZCvxdOH_HR(mAZ}@O5^z4{vU1Yi}Ruxy!qyqtossi-QY{X2W`8bx2Lml)94Tl&FP3gG(n=oYW4c%9F(h&4ffqlc zhQRQy5)$t@2;hh;^bE#%zB8(?C<0lJN*tHrkUa1zeqa7ZdJIV%b$~7JdFVgpr#wCd z6nR{yE_vWp_d#BY9z)W8SA)0Zy#RT{IF!e5kw<%_E$%W`QY{C z?SNnMP{zMPUXmU|5_c0Uh9b^To)c^)75hf&Y zUqS$i)Gy&TBOUdikfaORb?!yFzm!C9B^_OF?DZn=MWxse=7ObdhxK5)v#vO=PY%xV-ntUtrv|#@%3C-cyWkGj6YOc|SA!Hsk(*ad#W{v&OyOxQ`n5 zr^fxYam&%%Ebk2CUTxeD821L_-fY}k>EgVfFz)A#+evwlqw9}Lrt1%$kUoY#aiyJF zuvdF$kG!E?dLGemR`mUW`f*)A1@nk_(~=h*%m=XVANJ#E9aG89F|O0XYLZ+G`z|_( z)pHW&8OyOaKbRRdKfuhyo*(Fmip&p8+-x%ssONR^ngu?k7Hhv!zYEuS1b;FQU^wOh z9LF#Z;Lrum1hUg$9`FjCO7|0RFb~i@s3NXAoHq~n5F;)p#&$W>1N>710Q~DgHtxO+ zB>?#U1_F07A_V-Ofp8oA|C3H9;GYAu0skL_SIzh%`~mz^;!NzhuADmfoK=pFg_pH; zH1=;|P&r^ekh(_ttC^nY{(O22xL31zLdDWg3Kkz#&X#Y4;&9$iLafD_S#fSLW8v@R zV0l-!m|IGx=Hni7xi!x}x#aX0fXi{=wno~%?8$-L(?HvYz*7lmhv96y(+Lu&MF(Ub z7NpP$zfFuI1#xz(4$c2bI(>KNgB8>L(BdT>b*=ar-3W$vkC1rJK>$>kPY1vp0LM{` zs)h9k!`VH!;|38YB=UkdI}IEb1!q4Gd5c6CTxq|n;kV02Tt~gi@-bY>$KgxL0I!Op z!K2{pN38M@ZHc@c0ePoDUJ_wKa;_lG9<|Ch9?m|W@pWrE#zRH+i_7 z#*K`Hv7q&^u<*)MWSjb~5G<_Mq>iR@z~qm{;G!Qtt=FZ(k3M-0!<^Cd-NeUSpL#6f z;j%D146C}0hqaM?mI1);Ma^(+KyiKFrL_Mr0Wh;30)RR7h9Ueg0PrcuQ5lcxesfDL z3Zf0+JK>3fgcbqbgZ-;lY_NrMtzqTh$3N(JH(y3cgza)h}ywXBbwVrd^wrbq6KCyCn+w|oq z`>znv(&F7>I$0PtmfpYwhmDPm?5U59Z`?Z2*`h7*)8=Sx<$goV(Yk-4V``fSJ6t0U zy8{wekZN%-onQ;<6`L4G3gYBq>8OV#5A!kIx9Kq?ap!>-Kch|u!#hbxyg{7Iea@dn zqEa}SdkFpy5e8S}T?#+3l+cqF4js=P{VT{zN+!6{enFh9?d6Csg_HU1q$oJ~d8>Tm z;pAnIXO~av8^p;kM8wG(Anyzjg8I>Nn zfVtFq{Dz~4mo+xVZSb+*TD+{4!(a1?nS~?p(nNeV92kth>~SWr3_}U<^3NDo>D~zk z24k)!0bahBoM$lpFuk6^xF22^jNd`0!sRR)t3?(f2k`Q5m@JQ%IZX@0%T%Yf?YIP9 z{$nOL3r93wrisGY7KWE^4B}-IVBzJP@_6~?Hw7>M=J4@y9uVj8vbn|36yb|GY`jtc zm|I#TYiYCcG-8grX63hf0Y{i!+3tTBk2TfeA0x#yUPc>CHeOERoI<xWJvR#>HF+Lje^ouA6E)~c#Pw&%yZ=I0yr(gnby8%If+xIbpzdqwt29~>HbShoi zdjYp!r&AFpsx^zVchc*@?H+gmx2a(Pw@-i*kH7Wi_ne=C6vhsJX07+(O5BQ0+} zDww#SoU+3CLeH}6uWvqdY4uxo8FT3CaZ_+9v$ji8zf-r7h*#_mN7EaLceIVY_)MQ^ zX=7o2Koh+N;Z(x|B5BeOcZ{EoV2Lx~r|eScQdS;#73Gc4V@Tw!1}}a_Z2`l(Oi1Er zZm{d|D(W$cFxG=@AoW-ezvO{et;3CbGd+eRj%%ZBdHZ0vWxXhmuE=YLU-H1Ko`F2p zO^E48AHZSDdj#?l2%|g{)t5I2zb)@|$U6mLLK3$X0k*uStn%?Wmz3|L@Jk-bco!~A zk{&}6w*$N_?|@Z4u7eeM9POD$>i8bWJ5hv~II??;AC9IgARr7ge+97+(QlHePas_s z%=}fPquo$Qia-}l<`cgI*N;G|EVei6L6>#Kd40MV7vVaeD{M0>xS%adV`lpdJRi@V zr$g5`%iZdwanaLo87y-2n&h+}BDMAc_+1d2T>S(zAL9Benk7A3B2Tn2+vLK^EFt?4 zL7THY?+~8Pii9YQ<~F7oJ1yiz6CbpuP$ zKpBI3x^Yv+JSMCk6ohC2i?vMgL~YQ{HWH=-i-b$xk;LY#+y7 zH%>pWd8>!%*ED@{ET!&wiCCZCd@olEGZl$bGi4Y~_1r=AkEujKUTyN;=d1wU zF9+Ud{A&gH9+gyTWP;c}?9I#ovRB#=uFvAZ_I~a6ZWl*iqe3frBjN$uKLsZb+h1g0 zx%(qJl`cni!1iC#sfgc1=kgEY-{oK4G>*PNtUdxKcFo7}@3I8I>JECda()qaTH%sd zCJ(3oJU#+{`IUFa0oC#SYA}-m#eaUBQ+LVI_tstV-b?DXbj11^&N9ZM8-pIl!KsbLxa_(N4Bfv+E(YTZfVgLw&I>5=rr}Y6;?wBc zd%^FJkH}3u^i3U!drXg^>{G-&b{`{tBn7eeV*n)_hdCxQKZix8J=O4nCLRKB6VsB; z#KH8X(xs3_^1!Po@Av63Byp?3i=R<07~WMv5=R|IJSD{E^^bsIJ*Y>d9zpCq3IVrB zCb%Lmh`o0~Ke1kvM_1&vTY2DB=i3CEy3ie)!i`4`nrLgxlRDO*wJ%3&+NPDF`WZo?7 z-KtB%Kf5$+ANHg?9Zl@b3qaD*Zi9WJ*u{HIT3jOEF22cxX}fj`I2l;^=-I{J zK%2O=KS>muIQ0@PF`H7~l>4+FYZ2i(qxcKh#4lxdh5M&)^58m#tN_=)VqB&B1?DPq zXHjy2P5hIL@NDAS;RViqH=T<3ml^TXvxdO&=rIXvn^0G1*rrvchUV+>XA%zS(ywzBpP^d1KmhlBpU2NuC2YNR5^tJW2w3XTe)F+1^k9 zzrnMoPg~Ka+=d`wINt7n#G<4ij*rg-?m;+p3_7=u2ZK2NCB)gpw4@-8FP1Kaw33JU zm~Mm~LlU@w9m- zMHpO>w*!6~$2VH#8xO~~K;9w|V&swCW66eBeH0apg5$r5bb=L`PeccC{68UG6dYfP zN{v8BDI9+XE&}aPLULZ=#kIrVEF7Pf!uYM@1-y|sejiQ|#fENQWA^V0{IZ7I&|3oW zy#c=HJfOS4rmj_?%mun_!}$@|*LhFc_I29lXkTAQ7agjZEQJn`1=wUB;@P)}E0~VhGKjT@5N4BLkRaABmM(?Nl817b zE{e_k?~smnm5|5?V(k&sgXvh0@v!zsAs`H61+g}Fn~j3CIifC-!oihx4r1*l$V-^= z!Rxnk5Nj`jyd>g zz_@xFljue6hwRTBFkQa!h$#=1rl1v)ceL4(m<9}cOX8!rA+fj0z}t-2^BWsEPD?lh#MUN4nyu_YT`eO_D7%XvArh@a`M!SNw_3`yKM;HiUz z9xPL8Hy8{r1$&)c3SROsE$De6#oAv4hUw*N zkpOMdFX?J=oK@m zkHOV?4DMPJuk{#st;gWDneZ*f)q^g=^=uukc|Pr{oKkBMr}fPw&(Np!P04*Zr}YhP zZqT&8@!DZp-}ql5IR^Dj@7A`V&Yo@E-sg;__09DBCjT*-BmLyc^!$b+py1)@`OWnF zW_o_3UmoAqJs52thUxju^!z6BSIt&U>ziqPE}1zbDqO|e&eg0X?xnVJ#E^a=8Y%Q>jkFQ z3*yo2>&w)AO7BxH7G8ru9v}H{^ZO`sN7Mr*-sp zwGFg%v~_Ih;);P%^P7fcXRpArX;ABoTQ699q2uIR-@)4%c}}S$7ajrvcTtfjShy8+wKK=zn;m6^8&Q|fRx|ZS!qvk#m$&%Pd*{wOyZIX8A2PCV2{o5y9`erJ zj~VE{bH~-Cc<1h4=v3$h)mTDZ3l1Agb2NgA`Zc+D{A+L=TIHNFehP@d2mbe$wRde+ zj$5vyo6EZTI+YXmxOJ*LUdNQJIAV&6Q4d*t9eML&dkv&MeVJ|vcW0G@Rqwb)T>EP8 zc|l%?MNQ)H8t)PKZoz@?3+B59cyEoWX<*IsPa2v20#jUJ_UU!wi_YKL*|lNo@IXh` zz)i_j>))4bNTrrvzidhU>FT%oODNuZa_qFW-O}z?;;%&-rjr%L_S$L$`(saVT}H8V zi>*A&&vaMOV~FP>1=pQ_5@Ebcge2WL2(Z_k@5K7whY(hS152&-y@DXc_unKByy_w_ zo%9%zI1UN6yv1OcpYpg$n(adV12`lPylO9)Bt3?t{b=*D6}&BPEf^`Ee&B(4%I90Z%)>R? zqyz>fOAYBb#(x2a$Y#1~bR4@cNZgkYhp2w0AzcFIot{_!OS<5C{LM)Bmm%qx-Ww;q z$Su5B2%HONHV(E6>&3c4gil*gxv=*D`~=1$Q0C;Hyn7L%X% z3gzha{%~(I{Kx2`6WnQBjw|HZ2co>ER<~jqFjk&Q;O%62noZn=(b1`boL2za9~*ga7(nC_b>h*BJpJPg*=*Vf+K)zY=Ct7CYm zt7UMgt%JkvVS=%7;a5vb=t+*s{s;NDE3l#YfSQ7@^l{)Yuk>j>4&-*$#x?J|M3u+T z$(9b@blp%}JKUiG&)p)vslPdJn7u`AUv@$9*TT5euoJ5>q+SITDvX|Emr+lb3T)d*Uq9LH3^ zXv7@Ea)*o6T80MNuIt~*7qTOLi*9-ibb+O4TyS!fQOFboAk@OsdkV$$DnV~p*Hgwwv9X5dAG~Df2 zzUE zZNuLoHtqjnAolV7ceu`vomMy`jdb-VtDkEHiw|<#h)-I3-bwr(-gAyqe`dWqbE$h% zcj~=()G_{^zAW{cS@DaxtuLQ>c`Zw;%|~%yZ$q!k!5?nwmeyzdo2K12T%tce{U%H* z^9^*QuRL)OnUH<^t+AQ!yHI`N0NSWX!~u5S5*saN;kHe6p0TOUML6?|O_g>+@`5W` z;kSu#q%(0)E0jvdIfUeaSGD4x?iG@_)d&zjL`q2FxR$||cfe{F)=%WM!!LO#<8?5L=rJVi`~mQ`yl25A5XSOhQ1;~w!f(r4 zhyf&tFd>QK^I2QoOIG=+5GUp1s3UnOV@(lxI}m5fd(|o*w2v?EGw@R$uOF!s@;tra z@r>iK$GBc7zA1nX5oQbj3KAfyU(%()yi*vsltl`zQTPVZJ?cg2e@RCdMe^w-K$Zjo zPe90cYZP9?MGZTzFUWZ0JY-AT!cjSQdsK%L?(MVbwR; zVmM?RkdZ;-$NLyrxIFa?1OY#Oj2SBQ4FcyU3@mpK(D6SB@iI8zM-FvipM;Rjeg!{{ zFmudtTH%sd5brw>URZha-SKsI#|a}{oyY`CBADDB%Y(_a9+*TS(M=21u(A&du@Z;k z$!{Svk0;R$b@Dvk6s?mVrPTLPHwT8kHX zs}UxCMt>Iv?11Jf`BM^@&(9aJ5U~7X_t2Rr3~=uHK5d~ zLQF^c01mr+uYgG)jPhpS5P3m7xiY3y67fQEt{|R#-6|i)Cy^J#lbedj+krfG`Qj*) zly5vdxefB7;K^H{IildnhmkG}*9Gz9_mM6No_swbo~%OU!_F)1F0Qmc`L{Bj*c(St-w#a3XZILTdk?~~O8#IdINhdXu zK~d5DH)P>z{7C=n2M<2&;=5wDoigHgAA)vCr{?f2la=pQm(-y7zQZQOAp?Wl_D29P zHKZ(a4ZysKCn#;8d=kl=5&pf;X@x^#Sv-unww2!I@^69jn5MMT z7;~z9>^BT!+I`C##t!}dSd)cS*w>jZ3}g1PgIXlIH;6HdrK1j$JQTxpHiDM8)q!;X zKX~3vLXs|sF}Xf=9}<!0T9-u?p3~2}m6{({ z&Dq}^JovG)1Td$;ohkEuTSr}y{T1-&|2=qczET^rzeGCl6aCihk>Jt4XS3swL4=oq zbT$r0UzCzNnnKooCi>UUKc{(62-B={%H0 zh?QD1tLjeY_SjB$)2syVSMLJ+BYb&`imt7*sZaevg%R zlaRy(G3p(tM-+_u0t9dz6B2nrj2iVDxvjY27mF~s(#{`%-^P?p76ydZZ)diFlmT8v z449VHWMjae(f3xhMD%F<3Z6|h6jZ2Lk^wDG34th4a#sqOE zN9a86p9JS^2{q`n!cSU>!^52et@LU;sOQyo&>@{^ z((R!Cc2w=4rfZ^phiM0GX_2gx`A*=Qw~BN6S^H6c*3KJD2r61r5Mwq)#F(^O+9cQ~ zh%t+$3t~*Bi-Iwy?Vw((2QlWf9n@3usW?VH8yptPBa9hCOc-!40-!J~R}79WrP z0Bz^g@NbuoM>);%@aV@|;kE3b9-p^^euCNGBs=KGouh6CBX~dkNS?d@1Ga3iZ6&6#98)l2?x^X=qi~*6=0voiV+4v)Ay? zA(UGJw6vJ+34gdPR4xwV^Qoghd4FSY=Cnz4Y5U-yTG~0)9=ofswV`$&D~30@*E$|m z>NSSEQ<;u@@VPtnkxlxpMnKReS}dKu>n#CH7sV#ZfrxjNkeoO8+*z z7o%~G1)J!NCbV00|IX4M(r$8Ct$qliZO}>dKs(YTd`Zls?^mM+W!Vqm|8fWmM|}W8a3Xa>yzh zNjrFUHop?*sla*u73)YpUCX@dkhcqYuh4m4#k=$>xjy5hzLohI(v-np4*$#W^E@;4 zExRr^PrFyBCb*GP|8TNhiUOxSJoc^ezZpi&wuVvNm?4xc-k9Kg;4)NAj zU5|f5o^q723}t-6oD=1k@2Kl@D6^Lb@p^4SqDzxeF14Hf6S!KNZJvk?T7r4 zu1w_!&#@`@_>t@vP~$i?+=&VsP?bR~9giKV=)I(dtGX({n(rp)MP4T%u_Qe%pA_oC z>|up`7<&spj+NiVs;cnwSjRW|CsS(HOvt_h%|AOI%B`*v4Cgjj*gs?@-dv++j(MwW z0d5j+VD)AbcbJioozwYi{K;$rUd|ut7+&G>zRGV~@EZn}yFa5->D~zkvxy2w!fZm{ zk=|@V-?W$O*+e(e9I~JnSCq4fZy=8Mb!F@#r@VLTjS6ln_}D3`;taaNV!`e|>2&Lz z;|5i%S#Q&ITyL+6t#kB*V#Xgc`Fe*EXP$s+Iz#X|xCA;Hepc6cf7P&ZPSj!LSO$FM z-SMg$`~YvagyV;iWWGZg$KQa+w`=dL51Q7RePc>FO%6*_e%0OaO?Ss<-&n7l)n15a zWmmI4iqyDC)a)DaO5BI-)GG1Uh`R%Edv!ddoo>f#X=fHlOFWAl&m#3JI)`$8&zXb+|hyXP!*zkF-A38`~W>UL*#uU)%yi(7xG+rD(| z&YrsWy4QBYaZliXoqOD+Mdo*Wz9+=%zh>6FJDodXA9X(xzrAc{xi!yAqCtO(q@F$; z?|FjUHk7*-$1ON`DIdWh#4ujsEjZ^+9754&gIjtwZ#gSg%4W{cMPdR zUL{;})o@?W*1qY@DkgKbIMCL&8QW5r87eM~TH3Mox+7Xn6FPUa`z`dfrr>&-i}9S1 zbD9%yu-h^nAxv=Y_?XqF5$(@+&NS%-=NC(tvhpxL)6pa)B;r>iK>UpR6Wpb|Q-vh% zJOlv#`?Lx5NFtK;7;oVY>alx?_eX8FiQ-6ST?-T~Ehz`y% zzl?NGg`{J8s~f3GG+GjYz})(}@VfE(uuY^d!>bmdUxl5QaW+YL81`12W7>6fvCk{} z2#I4%lxG}f%H^;1eg%0K;NnqN(&ar{axXPpi+3;`+|!NA{zm>R<8oXee}Qq=8h3+n zuQqO*aeIxc=bp&-VZ+~U++D`~tZ~_YDgP(N{e^Lfk~t?mXx!DP7w5A5#@$61@w<)t z4dXsx+qbIe(5D-1k>upI=lJ zrk>^;T(ic!IfuPA3ibnj6ZYBo|JaN;aFe28y)8rO9ZNR=Pwc+s4O@rqt&xJ#C&rtLgEuA9kw4Y&f(N#M zw@Kf?2nb@}V(F->BoE~<9r2%##H~hv_!;+O9K4%^B#yey#&9da>_ZsqF&+l~90Y6= zVQ@v>W$@eb?EQAx9?~xDRvviO|6}ia;NvQ){qNno$!^m$-E8_NEd{o;h5n=6wt-fx z7@$aj0_i_mppf)0t@KYQEfhrzZJ`Az77JLBKZ~Fug7&98t%|+~qSc4K%Ht`|hq_6K zwD_+oDo>IAzTcU1_Ren7WDA9YGM~MB&YU@OX70?~xo76g%@6$YsJE7XB$gkipTzwDnhf9!QM4p4920pu0xz;Vc<4oG>O>A3{QHsL06@pQ*!AN-Yyc{g%RJHTKgnJyGuv?n+HYjruPge0 zujmDx&}X-R%P(3jau3!zxw?pc@r zEsF1h8tR^1ogJ(dLV0<*SeLsPt(Ho;heY>$=gf@7>u_xEqWNp)dvDXlJDusCZrf6w zsBr#uF5bZjv^}Xtd4;-XwWHg0rF&9WEJpWCIl3LtTsarczw%oTsZR1&sC%ZJ z{B@;!VrgNg@-`q16j{#Ee5~!DE8X(}$SX$od=R=~F}i0Ke4Xilg}Udf;44P=9A2dE z$+1dj>E;SV5N#jy{xj>Ib-2G`bx-%~;1uFjZ8QxSbGf_%4Bq1hU^KYVFR#HLF%%p(hA; z|NYGIjP`9@i9uc!G~S!rMIIFvyQx>6Mve#8gkwBNBk@#JF- zhpZcUZ#h?Sc#q>kvB0@cx97bF6#ujMD_E+*XB&#*?TnE7!o*$@O9RdqV5!DO0I*c! zRf1T+4y?nXj7mIS@o4nVWEn+;sU*sZD)nRc0??)%HyQAHdae#UY;8{pSoQAQlSDZml!gHeV z5NO!>z}AJOTl-GG65$n+{k{R zH*^uYXSYMm?+fj-)~(&QO!O7N5lDwGOtu|DqptuOeFf0yD}d7^+~_Ot8+`?EzR;Hn z~t`x}k9Q_%AUm!#S{<8SLO^8OoJ|cp@`Xx)Qa6Yfv?f1MVq>Hz+zRZUe&CT{h zb}g}&w%<_o(m@t$gMB}4=E6?$qcgEaH!~#RWhP8WXIh4*?rjeePrm>SjC?KoF=rO{ zG8=Gb+ZY}%2xmgCN!K~hT2}octMf%I(Rk2Mdiq?+>S#eM^0NGetytG;wWjjH$XhSF zq_x6ToF+rJ@{YsPWGm#iR_y90%IKwyN59$=F28V}@0l?v5Bf*9>u3Q|8AO3#h`-6$VF<+Q% z!%EX%o5iRw@19N4Z;g+Sju$(Mgg+WW?6DA!GsPaIlxhS0nPoV&HRr2Ca8I-D7cAvo zFUqaR;4c_iGK{hOUd)mi^GCeJzz&&qmN$n-eTLVV{29GJ1pX*|UiMF8M}K2%B#PXy z+V;cwo_|e6c`swT0m(QP6Lkw)rK6B-yBE(sW`<=@E;#J^7|nGI;j4m0!>*d$12jBuZtdM%LFaT>gRjHS-sA zvWgR7$lXG(=sDU0ny%~X*Flkd$9}E*3iB6n=KBuKlFm-obh-^)n}6Y);`8WC+1R;u zkh3*jlxC#?)dmnX&j&)<^3BGaT<0i{E9Q^ba?n=t5cNkaCXS* zeuMki2M_Oq?_0f(!u^2UAGcW}0jj)nfxGga>lu~{?@#3|aN+>Fa1HN6#1gLz3sH&XXiknY^$k#Mwu>4p*EEA-gbIqu%p*deD&mkF9 z)1jw(J>5-#=`r=tb=1q*qC*$pi9>Zc&Nq7Y+#JQt4dt`Nh3-&%7dMnf`Bk~o*VyN9 zZgQZ*z;bo<{P|YZm)CUoN@D&a$s+Z9G}0r<4>&>~+aCQ!&j(~5LI3FjrwN=Rkk2T? z7Ykf2@G61V3cOz6%>uUze1H&*>Oq0u5c>BBq4z%}{$~X~FZ8|Q=Qx(>ucjYz`VgY3 zs>RP*=eR*3%aMH4F&xu+_v46~@so;OubINQ6fS=pfwh^@?D6ehD%0X+Gi_+SsNU(V z^rzrQTP*X^3tA&ykoDtrZFq(-#;9wnZpIj+IWr+W2z(er`01x#C{J(5R(Kn-(d^yN z+p$JnTf7-#jpocYe!qf=zoxOpTW0TFIObU4>w@PFntE2 z4El!g-dk=4FPurSJ(!*KZDdiwnK#^Tf#~3cL=0Ze0l?tpzpPJ)H4uWqi+OVF;AJo= z@!(Tnz~H5piNvB0(`WbUp&iROCi*d==kX~BHsTLMno$I?U>E>~G&2!d4gvWD1DtJp zm%-0%d#4hfVMZ;HA=tL}C&Q=P_V^&lw!Q0sdS#E}58L*h0qB`wMt=az|9d!6VjkW2Uy@Om3Hs5C<6-tAGgOoRXf1LtB;pF8~(mu#vg`OPcW*V{4{`f1Fpj`w0br0M@~31 zJ@(8A@e9UIMcXusGRlz|cYMV@NF57zYDM%GW;z^aeIMTapqQafSIZ@|fl}O1Ty?lu zP{q*m=AU2F@X2nGJ9J~HZnwvU@3E^^tzP8mZn<1Bb)3O}XTUXo!GhI`uk^-*^YzPC zE?MRJmoHnn*!3@2joEyc5Ki!iLgwcWvGRR~X23-qozDf|&2*gHaT|8jP&=+4F2@7M z@r3DU0Le!`yHLtg&F}%>u45RT>2U$O<4c1_<1ilO^>YY{vO`v!eAtNZ@d;O_D0M07 z#_IauK2iwmZoIVt8NEp(0jj)nfxGf*@Sf3fA=GkN;KTvyB@i&2h&b}mQI~V&O@_P# zXx)vsCP6?kImgnwIOUDx4%<&d0toZR3U)SM#iur#ljkne_KX@3n2)s?;Ck1{= z;3EQ`6!?x}@bEN{uNqq%Q68ifDHW1?ZTP*&|1%62A>&5>Sfd@T4!yIe4>ZFKu zClkiK=C#k282P`Z@sf@TI9#A@HO8+8RyPUotBy~re;h{YB)5y#q+ONJR&vxv{j21&s?>3^}@xg zuTGsZ=&Dt)dIV6fR zZ;ZW&t2e`&bK1iU28Folxzw*+r+mkONPV$8zR}>(IFv(q^+d#J81-Dm8MF--8@M_R zI|&3=pPP$JJP2BETqU@mj+uO&1*ma=dQ;(1S?2*Kr;nnRguL@m&|>wJ6GiGVHjH^Y*<$4%|p(1oG6%zA(Q(=JWPOuAP}f z`Op^F?g$qPG&&~ySBU=u0GZwr5%0fV@V%yN3TG8?&f3gvCos32z}z-?$~)m< z0W7{ae{qEGlM@h>QYnXnb# zbWARD{*zZvul#H<_Trp1rfT~baygUrns6P4soF_#F3A#P={OudDOL-KV&s0kMDL@ z-ag1HW=?gDlRx%pn!im2@_M6UQlP2Rv^Id?mftl_{<@k|9eV(I4?rGev!3bB1K%;4 z82m(e70#(%j*NGv3m49*ehz$mKdMvFg}U(fz_&}80F{prMVoWOar4E@sm?`Z4b8<0 z%N8BF1Fd1H%dxn*;j_sW7rKtRFl#(?#e5$yP?)PW49S3q`b5AScr3zh5BRH0MlduO@tk5c>H4NO%m@L4J-G8QzBw zRZ=Z})^4Y3RZv2mu2q?sN}VwNT~OKW|G0Ge#1nC<*n|n=iq_+I*(Q~z$9rk4OG#&Z ztS4zpHD>~@dBUbq?82X znMf>ZT4&*?A$mJ`?0OWwGU6rCsif51ga3lD#OaWOu>`ZlsmjilrkuvO&eD`u4WBMe z;d7C1Cn8H@WE%p{rHs=L9!OBbJf1w@Q!xE=+IPd)~4xYqSQ zR^pEvolcL54<0)j-A32z`q`4gF%7LSWw^|krmOWP+5i#Ts448CYH^WH+SPqm&sPYy7*Em4E=O8adM4XoMCGflQo`O8~#FW?7_~#YKD`x!j zoRhz<#y>|OLn)-GPV>G21UG-rJNbjEmb+iht7{y}c=rM1Js^B(GQnL0zG+TGqPz;n zKc6kh?_4!X;rQp5;5&+f#VKFm_~#$s+oeo^%13B54jb5jN+@RhvlZ=XXeaULSI3M z@zQGX-!Jgs$4hHEw|g9Dyp*1l#vlr=`?Hu|!gB>zFkZqQ;S|^H&k*|upp28o7uY|1 zuU70IGjd}j^0@X7Aisg(5};Yfp7 zw8oGhsI9|{C7#MNhVZTB|Hv@(_-2U5Q^3nOyq7?KM$8N@EGq{WUeBWu%u6>6-v=TF z795>n!!R@DHQ_o8rjj3)_)8p9iP^M!AX^E`DI8aDRhZaH%p-?DYpRyoH-7$U3s3@I z)vB5i*VkoXI9m+EM{59Jr{fNcH;UgDY?x`4W!bSe>`1lZM9H0laKbTLl zIzO=3T-Q=735FvcLUae-5bw{cw#f=H@b$pUXGXw!%KASlfzyEt}amW94W5{~2v7?Yu8M zRDU4E7=lYr=pTk^B77}lh=a1Z)D-{C(n&F74v#+-AP+ln_{~x%$88?=0{f|qZ}tJF zf^2AinIZO<{BebAf8m%Rw7-O7i+AiV<|N1i><50Dt;5uU9jG)I+^&50e-BT=K!r~a z1}gsz5DrvMgpmA!$`DfGff)~%1gA2QSoEtDS2$3KQhOHr%SVC2{&EH>*8Vbzl-$FWnl&`{BCzwZRD%hze^g7@ zc}&CAfGdS-3a&wd0~psCUoAok`Ck9nD$k^2MmxNhLo(1hbhe#$MqzK?UBjK*cp6MI zw|`N=3&&wY@f2{JVfr9I^3h*74!aFB*RfBbGu<&D)NbPr#8)^DBVRui0$fq{){0Z$ zL-4Q{P^W0&IBYcTBZWZTM^`qUCS>#`6$YsC&IRs{OWb|AEDtT01x_5GUO56%M8s)1 z7mmZ+eYsHWbMgw;xQ&K@V#Z;MpwDT&u_M#`6^_HsF36wW?*_zi?{_)m={OOpVNPD* zIP5aWt3$XtmG=M$#C06@4e-s=KnbI_a2(bjl{E=;bsA+S2&k$?rJo1BV#Z-Bz_&{y z0jfMgtJT=R-Kd0O#$lXk8`_CHhh$Jqhn~YV4h!d9+}yAn+2TU?My_#dKr+RT!}uBu z$6?I>TX7t=h55om!9I)dUV)zx_=rHZS%&WxxL@GG+IX%-^-<1Egm^ALD*h(~9`v}a zaaE%^^^v1pY#<&t4ky5#m>xHAqOOw5Un@6$Lhb#0Gh^s&Ujfgl6E8Ii8 z6q$GsJiT#YnmVW3odsOu0DVLD5OW-%PSYyXjrk2omJ{`uUz?oHQ=FAL04t zY=AnVygnpQ>Cd9TJJTuIwYxec&-~$FU!96BTsK6WoO-Z2y&Ozc%RzptkmxJQ6Tew)O>c`d-<*9J-t%FjaF4-AxXkVh?intakuxX%j6mC7?Hu!DW)tqcxO}5V=c{n+(V)Un;SN=} zzmb@?#{V~@#Dlv4pu+L6ddx9Hf%fFU{L6u21n|6UE} zdYLb~0_Gnwe;Zro`TH$C-EIvQ-#jm#1vjrAUfJ0IJvnah_eTCi;FmE0Zw;6h2@b58IBUu|XL_X(tZyE3#nmf#0{<8s zsAAz##kgvH%|GS$E!}oUy@9T9j&rx@7i{xcHYILFz@$c`f~UEv((f%vGWXdK2P zADf;9ll4cJr92e^KT%%y2?VeXH`tk;`4B=~ zJ(Fia9;I;rRdk`Ac|Q2Q-U%Q1k)@n#LS>~u>Oox{hblu%(9XK>KsUa&&V;`D{s{I}~%5EO$Eg9h|E?+W71r%hNgQW~iJg z?`V6@3(>q$Y#WF`DDSje9Tw^cyK&CzjS$FoNB=~DeD3K#UEnlK!^tQpuk@VeUbS2{1&wbS4%|}g>A1e$h@0mJb< zg(`2~DaY^i8Z&K^n{eiPL#D;6%e2I5q3dFeNGWvU66n4B#`=EG!(QEIUx?RkY<;gc zt1W2Ul>IaG;uymBo$}2++>cAKZ#})3CuG?t-Ri}4=I%Z34Cb~Oe+5&Ur;;8Ez5o#F z#eALQ>%}jTEgqP9F9|A`NSk0TsEOVm9%wJn#nt(lLux zMeZ4JAYM9qTlD;GQR>OPg3A~<%j(JEo8!gzfyztu6SpWS;?^yH6} z`eV;LXYAKhQRaKbUFSWBxe>=(ol;SKi@!OtDYzkeeaXgBF9n*du^!0&-SCA9XIBdr zC`(=btfVZME=pfMq}xREGamZh(VPE8zYCvo^d&UzoW3*+IQ^;#Y}AGz! z(%{uNj7Pp=^k%Lf;8UkgQNBN2J(Ax^-vt`qYhCHhN8>?eSEx?q73$3|LmpJHZuREv zkjHyd7Zrr>Z#REDY%c*C^G9ume3W0PH}8VHp`feN{1xiW3CPp@u|HFJrt8+ofWLvf zl;c-<8$fgOSMB7lE4{fK4Pv+ofefO&?h^=LTn#c5>U&|`KZH<-XcRVYIrxrJ51@)J z)SGVtUom>~qbL;G{JYbepU2I=yOVSsl0h{;^c*g|d0H-0Zf@RKy_rd}y=vXm0jC~4 zngQY%-iaP*_x|mmf1P;`#`q)n&NfrS$J;XayoWj)M6^0%BG^YBB|`M z(V4d1(53H4t#6$H9otVuS|iY*&3QJj<(u?l{P&jIGa`>GKeMXRPx`g!?@CL&z&Sd` z`y7kL=FGI|;Rg4-(*XT@8JIsg%N2G9y;i9S}4SN5fwO_@*eT_m%qTvFYQ-UOEQNs_S!3m`^^Nwh$iy z#-1Nfm^LN&I$uVsMa}R|wbCwUZ`~9Q$xQCQ=49>Lf1(zxr!!l++q!Jm<8`p5&tubb zoaq7JKMq$M7xV+;4#dZIkj5b+`7R4odv%uZz(*CPkAw1D(@2D8V9KN96Tu^;Fc0$$eFz@*iRx5Fp)UDJ@O?#@ z0F{r>_NO-RE>;$Ze2?QDY3_sf#e0Q_oVyE!H!$Z5c)VR~+g^lR&| zsdSu^jd94~I5&01cWaFPeD1)UkRC=4ObNJtmPF_Re8rRF2D;(6s4M2{hVGlA$az!@ zY>ci4I07;wfNVR269w`ar~h<;(*({G$Y+P)^93#!c!j_Z2)s_`DvT#DVwp89LZ_QFq3E*&H3I;Jl4}Q` zP;%hfaU0uKQqN=Kk)t=Fr;$>(jTG}f%YBJvG~__zd~R}s=Wy%1-hZOp zt0>l+!+-zp6}_N{Vr>Bg;aBw8aPG5@i27lHrIZ4rwyCLLN2wq57~^WZUGE#??XZ8h z^?Wu&v8J?N>1rlcTLaj;(s^A`;iqBpl;pf^UWY^h?sj*Vehu<%xR8{+_iI|r@K38B zyDd@Q;Db?0d@l42-vHco=9vMZP(N;PnSd*wdF-7q^0C`dr+n;g6lWNZFXB_BPSKM< zaJ7Xf2J~6bs&MgDr21Wi$9q_55JH`(EB$y6 z3Ui<)YNI&55Y_yU^3SXv^Z7imer(@kMxVsPRSw<=9tzbfS=Q7qjsA(iH%dP?N&w`q zXL^7)3cO9=Ck1{=Ajj3@ds5&_0*$pB^h#7N^R3@iT*Pqr&HbQ#bgNG~K-)9ae#_p& zx9n4?D_T?O1<7|$$W-=Qkg2R*zoGKj2Qw{k%)gd__lPfLD!JBye!tez=r3KDX*mwp z$Gu2))FiBpn7W}Q;>~LDnz%l~8(CU=Th?!?ZAI~pB;9YCI=-}aV>(*9AR86gltbLF z3*}N?PskgBt7I-^&Tc_hFtW7n_N<0?)vxH@BtKyXaQGIWr{EoEgXTf3bi6i=H>5?Np@a$IowjAM&{0J0Ir};yX-U zX4{F7wWdH8cm|fToST+pM*{kgb1ce}_`2MY(Tjp9i|%JPmEsmNjX!L1X*#y-XYL#cKeLve~(RKg#+1TsgCB<{pz@ z&fqCaB^I=zJ+@4mo@seE)}XU&xbB8|M;m3kG-X5@+oj$U%arBGdmD~6lxvq;@tm+7 zv0ZMhZC#G~*0!i|J1d_!WVmh7v~6jNwr$(CSXy^|y2tjWmXf9g*)6#D{a6pA_0P7n zW^n#JE|+^e5%<>T)D5}&9J<}!XAkE-Sq>~ibFYn=7KYIum3#G4xZjAq-`F}nGjs49 zY-z`GIMRqIlvd#CiTLYZSnfO@NpS0l^f6qPH!ISRKmUuJq z{9tQ*I#cDRGeJ-9k{00G{4@uzGuz5gE~YKvd5D{GqIr`66(Wcm!Y(3NTCAYAR zv423>`{)mzHU2p*e)_cW(Z)?_-17;KgT^lPHLbyVYs@#$d{=R#?^lg8rYz_=CEhsb zmEpG!=u6gsNY*MG6k5fPBJ?3b;}_m|J#qg;`tkE@p=m4yEwF!R7Q-tLTPS2^!}A7E z0Q#wLTLtFZdqT>f-d@k1XCmlGJM(dr?#4KJFp4@A^2;`i1;`B=5uBYEnTDr`ZXqt8 zJFRqL#Au?iz$o3Z;CBed#E1|VgAdEi&;UL8)WXT6#Do7Jr6jnVa$?c{rtj>*j7oja z?#PHzm6W4?IID(5Xy=n+kNP=*lvpqp0Q)pHLQXsy<-yUBYbd9L4d`+5;CP?X<*Tj= zkNNRp(?NGn{oy*1I3`4TTmk9+w-B25)rDV?eTGkoJ+H#^=~HL%kBR!%0n)K(FkQ0N z^9M&slj)N3D|3Vy+oChKMSCsu{2|dVgClZlwAWhj4~;TyonE;T5r$ccM7YZHk99>> z@TfrMAq63v80vi^JiWK^?{552+w&?P1XlTF{(TF7$?xOOKR@{l!K~yj2`);$NH90~ zXM)*D4o&=d$({rkB>NFePo@ZFBu4?1BKNblMXN?5c{5APgh8|`v12)Y27wZsK0a_CiPrvszR!T_!t7}$Vh{p&27IhD%d-heMKbR_R? ziwPb0O$UF_!S8o)4h59o3-RjudgQ>$@diAVOZO8_x)%2a9E&=z>3+#cS8$WA;3i$c zO}c`cbS-YvwYWE+DJS`>j%2}&WWkMO!Hs0Wjbw{k$rkqp+(XHxB!A}Ik;T0us#l;+ zUbX=%P*WBi&>MANZ#0Oq#%%!$Zn7h|$&TPAJA#|+SlnjE;@%MhpJSCot<)9=pBCc4 z21VIYk7UC;>KtT-*U{dQXM>nJA4D8I)&P?4A9*T(9o~71fxR(v#+>`)H70)s`M$V6 z_V}6+6UGI*uRSZcXw0Z|aO{}$x$%>0&Kx^_;W*6XN6z(wiZMaIF~R%I|0k~<6SVth zjXfvW_TeSt$Bqc9@Lidp?5t_ygTZ5hn?8KjIq_qH9!t&+l4k{f=4<4u!SAlUXCsJ- zv(jV7j$1f39Sn!ip9ZP3($nZ4H6b#sDt=}BnDN1%k%@8ZgCBeABYjXbC=GAN7b8s= z{^qSW9?Af5eCJH57&^5geT)B*$W6hG(aj~BN^gi=AK%!6)A{HN;g+C`IdZPAx5HpV z9E5)xjAurizTN3f;o`fc8BC4Qt%Iv^WyZN@d!;U~@| zfJ4Dcx!J(ReE_tF9loW&Z^rc)!ns{r-J_sAi;JHQe97K`3F6YiYFx|%Wv75MuDX|q z<5o4@a^UQR)z#OpMd_?qwP?|@n$BnWb-gu;ah%u9GM6>uy+c3OUtf>j7=Po!nw05( zWGU@UnS;%IYZtm-qR$g!jNXZ1L*Xfw@NHOwpw3(Ocb zu3EKvQT>{$8y7<$zSCfqhySyG)D$}{$6){DeC)2|UP|j-wrHJuWcQNQ^H(f(3E_wC z!>x55GPa>_gnS?CqFn+X@;0UlDws%-s7h@$2)(_o3^^ zOE(Hv99MUIY4B(qpdRIY$RQ|tY60Ir(9^NEQ>T0VDC4J)mm(rg!)U8; zC2cV8DhNNVz@J;Xu+SSS`$X;f_O;8bvVj+*Lx4{-H21wJ3@fi%wsKMZ!p6q>M!c!5 zVl@c2xE=T)7(%jy@b42p*YD9^jt0f>X@v046F>Kgke)&{lb$Aof4TUt5kD0rh7Te{ zc!T&G#n1YTpq;YzBfzPz9?<55LNzugz!BsFObf&ur}49|CjE4Q99htBmXrXpPo;mc zz~uss%?b4N;%A>lzS{)eEATS{9})Pdz$XO$uRt@qh4@FH94Y5GLX_Dkf&8`{{ht!} z6+-kU13)7jCvd62^#Zx)hv8o!#PubCdxg&WVE9}@(B=tbeJ~%BQSO8n3S1&^wZLly za?cC-?hyE>z^4WNLg0%6|00le$aob3M+=-JaEib>f%60|6?lWdZwUOJ!2c4+Z?RE6 zCdCc>tH4S$AkvQ%I6~k|fvZ_BC=WUqlC0}5Hb68wbb%%u?45rP*y#2LV*{L2lQORR z?_0f+G}-B30~}5JUtOlntIf0}nzyv3y_@&Y9!US7+AZxr6r12Ku?a>RFdv_>^ZKV? z-0pibT2?wX!HJ#N1V;~Z>=Qd^6TB>W5^aK)Wh(n#1e@SyYnQ;cwOR&#-r5A!_Cgx% z5w1;eq+@$Qle2agQ{=N+Tsxq~jfx#Y{j>v?71&;k9Ak&DHbB~3N-_(wsnnKM+TTt> zzGzoSWVZC}lit!7lS2bj=`F2+w`~v1*DuoUhezVh1Z*RH5?k66bqu?GPYPukpiBqR zwgvkk(!oo_+OjA+@Ye4c?b;7vFT{F^4)(%5u8j|62p-rAXT#oc;jL(DCzYly%tq^; z%tlZ*%opt)FCs1Ex4lmt@>9#Yz0KsO1ZB!HVO`U%!TXyl_QiQ9$3pw!@`AKWw`cs4 zx+UHSK-8Uo9O9;$FKb18ww!QHrsbr`u-l+cY5!rFqmE4-((l(=nyFLRqadT@6R3}2 zPMx9+w4kdp_4;Mpo7&Q}{!D!~hH@CcMCwy*R3<)bQyIjM!KTFar11;e7URF(HboiB zSe9bHoIt!T+op9r2z4+Tw9f37pDM6h-Uq%y8{Zz+#>e`njSpphBFehY*&A}@K6E&2 zktlOh_NguHEc^22E$u9CQ~pgXhr5kGD&NXzMF4&l+3buA8l`Z`Z@wm^Uxa)>l?w>qA;)rKwA@(dMVJk!<)(90Phq z?St_=7fv$@%TTD(}Z%@(cKj z+?q&!4G^}(?-0yN{wKkO$)6F-PIB!6>|Of^W>)?kAX!Sy^49VsPt)`-syqUqY6wzJ zLAo(4!tyndVKDAp0{SG<&m>`PayG%70V8dQbfwIC(0BOST;cjTgp)cYYj`9&rG>R&+R zXshzuAXNU4LH}cdlI<4!fnZkh9|UufJ!~S0W<*JTl%9FXI|(kSdLN4AS(0h%jNBSe zeueZ4l8+m2H2EWX>ykesn4A0+!JOnD31)bGZie{Ba3wk90#`Y~^kg*w@2T%`!02+u z!+tsAVZWU5u%9&^lDEG#9v)BB8V?N`8V~!2#>4)h@vwhrJnSDD5Bsmo84vr1#>4)h z@vwi+c-TKQ9`?7!L#A6j1@|_f0j*Jthy7jSVf8%>9m%`1M!x}$@o>O{4*h-yH?}R| z_bA!okQ^Eht68QtU28D3>5B2N8vUcBYmI(3UBOMdf}3;&H|Yv)(zUov*WzAvV@|R) z`dP_>8_9wj$$}fnf*Z*ew~{UHRo_C%rX;Pw(B6?44+r)Sjfee1<6-swTz0I{&t^w( zlO4fLb_6%s5!__Q;x;=L_XZAlj#XlfhXYz1oI`w}13SVP4+qhBcqFaKypHk)(Rg^Y z7!T_}^8G>M0qmMkZ!xep)p+=|xZgbDym7%!SncLw8%i+DH64zOjQ8WGOdEUr*ik{x z^{^0*9UHvlt-tj8;G-X2VoZk1RxAbW{ty4em<-Pfu38c#AO<$Uaq;uhk>UM=EqJH9T`uv;gID^&%X5dv189z7X0?wCBb@seelH($5WAk zVn9Tx?D%3?mo`McYG6a0Qc>5H4e@*wwY4GIVKf9|ItUl~L^wP;;`F6XufA5+1fECw zZK%>&z}2zd*WlvR0;iGGZ3e<=8+Nv@C{b|Q4|yz!D~ED-;<^j6cRKPD;H$y45j2LW z;}jUDWtJmN=8MM-Qs#18oSsqFj5wM%@~V3V^yhIg&!i8>?Xy?@4QQ#bC@`J(wz4To z&Aww(OpQ;cPq=)-@#98#r*OLRHElvV1ShJ#?QM#DxYX%$0TWK1O=nMaaW=(g99fi! z=iRtMo8p1^*coXYMkL>AhoI=G1$^&|c^-Q|b;`$iYS%Wk4n~@}=sa*cUD_1y>V=oG z3IVU8=N8D@?by!Z2xIvXa{ilfw4eGn*t%S&@>YNdJZDp+&5rRkTQXzqPH>)6=3 zvMDx0-XtesmA9@y-X2FDZasG&TYzg`DCT9zE5@eyvQsXY6U@oGuRvaZ1g1b!r)d@1 z6!{k8K!^24`;V6M4&WLGs5g;`IRr(&C1kcQx`_~0j7{-zB<7Z%%6l3z@E&P2!tnPu z@ExTdK;>gz!IyK@n1gQ3`9ePOYg=J(Ip)lpKDYpJJJ}T3 zSH3YeMR~6mVN=|WVxz6^LBc*za>W0V_^BW<{2D@pKPvw3i2o(=N6|1DK8XAkUYke}TZ20zV+|27z2SN501e{z%}@1pZ3k9|gWD za1iSi@rDbm5m+m5n!vdNuN3%sfnOE)guwq4_`d>Q5a{E5&2)MR93b#CfveG;UzaVB zdcCuuu)F<)Cr&EfmbjZ8J6=OI1~~f(v1gF`3H_#U-{2`?Bb?>f2nTgyBh37>?Hh#6 zfcu^fv^VfCvNv!O>>!1E18;NfZP-Hz9@zRi?hR~*{eZT+iDJ8ZH|#5gw!5hXvcU7U z+Z*_K+DuTEe!+gfCkpIlo$dDn&)a&x-*()4p)Ig*zu#4F@P0qq0?{4k_WRKWh&Fdf z_WMQCceiuTU+@O(_Z#Hw_Zw)+F|^ykAPqZR@%#N~$9voE_j5P+VbKh?^!4uPb01%V zJNq!I&<%b`-QZ_-`dL#P_WB)8o1A$w+82&doqvNjHQhp7K8<{xZ}9s)g2N4d^T3zC z!SB7K#Dk~6h7EpP%a09yKcTO~2EVV8dfv_W54c3Ds||i1gd7}#^h>ZkLDnL6^}TBN zbXVV(;mh0A_W}sm)kldv$KWaSxyR7IIBGUs0rglOFvP)tGS5s)t04|{6d{Nq?)%6a zC9wQB%&w?~r{XOBajFtZ7}`TUu*CcMw-$euP5ATAulxwXtjaqGE~>nbU~c8Z1hXr@ zN-(eTae@mfe@HOB@}~qdDq+r6!ytwZT-1?5B9(u%Os^8udC5W0`#8?5H0HMpE4hx( zpHs;>OC(c6P*+(?FspJJ!R$(^;V>m7Id@vesOLkkf;DeG!6jbuEYR11o|p_mC1*jI zPy?u2Nqs+}S6)dOFq(Z5)MjLjp@~)y6Tf5d-xxf%ved+_OcKnhJc?j;fMgZ)%th}Ax zy2|?q=2SjHFuU?=1hB2q_Ou39<@YSte-TWt3)b?%a7&vsyF1) z?aZ{s9hWv;F_oEg1vlvmZqgOpq${{d*Wxx^i+k1F0%|3Tsmw?g+(;JONEX~k7Tid- zxRq>iuX;8mo02>T)7p1)*%4Ej$&TPAJA#|+2yU_?xXF&i3ruT2XO)O)t<}LhHLbl0 zh9jeRLGe=4+Bv8^?91cl)?r%vKV;SWJFdu^Z(4gU?g#f^r(LT*CiuS(9e7*blipdu zo@=|?hu45@c&~j2UiW+O_=bS}tfTP^yYfN<8CSJ(MX@yrVd#jW#axFeU}6v=j`GwI z<;%nvz2GSaUJWVgh5}&?b3D%Vz3Li(EXUP|tK8uyp1{Q(lj-mU#JG*1t#kOe!EY|E zc?e&JOC9G8xlykXJ@ak2)IAJ*CoblJvYUZ3uDaWRXK-n{e7Q{lpSriCp)9N_vs=#m z@m%q68{S*fQs&O{ty}HA!C!lR$3CWwxEn~@1|A(_8zL*`cM6|%(qZ1nv&TG!`Fg5V zn2tB``Pi^e7$$yw%u?*J9mllj3hiSD;!A^9<1iliRyqVlPc7iP1w9*kH+9N)G6=3+ zX&P>z82i}g(aG&sVSp;{+yZ$I;E)iO1IvX_%jFW_jDs%7xQBWPB0%M>fFF3{dZ9lI zKFZN>?VbwlV-F+U1o)V4SN5?-A#ak%CQy0nfV<`Lm?Mu_)%(~2T=N3d`waq9M8s*> zjqtnjo^r~CqYjmKA8?IB5#_MArHF{rutNLTGfw_EHL3D;0M|GaF&gsr5D}+gM#lk- z?TfAtGPPa<;U~)L`vL)sy90&nmY+s>8lh`*Ms`;VkaP7Ica|q9H*5}*VKDHC(Mf=Qdf$Z<;r*29J8-}sZqy$bEI9uQ{ zLi8g)68I~DgHfKOzf0hG0v8kFS|%_f^q&gsjdEsqmB0yv$j6@qR zCZ=oBDdW1|YquteM9`Ji7!caWhH|f6L#EBcemm~9;~AR`+;4|{CXoi%H|jDir;EMp zGRIyvx)Xa@`P+0)UA62@@?o!a{zDzNu`t`pnXO9lJnHXX)&a%ZB+MiYSf;RzbA2ek{je`COZZ>N|W9TDj)WI4Ir4497a%=9B09M2xi%> zakK5#IP80y%FO-}L6yJ8U-GY(H(sgS!4SMocS zYd694NxK0z{WW)$vdEN?m^zQ80YSH zaAP|Xx{sFE>Att7T)LeZ=eVTrfW|qKt{CA=x`LZ@1vlwhJl{B1gPT5}an48e zxRETlk!{$GD8|S!ykyn9nt`Eun zpuZri=DXI1!8o@Y#<_>Cy(hTvtkL5-+4DB+^a*L~dK>$5yXS2qHl%GRddFJ>HoP6g zUbmlgvezx%{aLS&FYYi@S*C3AOk-X}*f==5V^G>?i_=bDo`O?qfQumg$;dN`dlZgt<{k^45 z&~&FF-?8DnrO(ZDpLIMI#ueJ|8lduRMF=%NKFZ{a;~FM@eT=y8!FA-NE41Mqh>sei z#$i126|={!0uw;&_0*}1lRdI> zu^cFmP~}|$oN=^!IsvvN*QvY}AOg?X@Fszeax`4)V;N}feN08V3DB5sS2ny_$eRQn zbt-Qi2(G-jjy&9Y?mo5v*St{1I><{A5vO4{!tcsk>Xb`YHoWZzkXLBKYjpD0l?`tv z6+FLk8M>qY;Kbp09b7dH|KL(1y1agw{^@$ggdM z!5jN|McMG8XaE;=BF`ZiRP#g6;j-Z^%JmIyZdi_GdD(p{pPR|P@(r@#6}`!=38h6_ z+EzkL06ZoB=fqFFh~ZqT!0@f&&xqgH>=2$6e;+ht@((2hzuEH!Nd1WPokEYI^Ps;E zA;MXo|4KHzOVDg-Gg=_Lju1dQ1`%P#ZNmm`Cb+n zWj(`B{h4r(z>@^768J%Zn*?qXc&|X+1NWr(e=P9l0%Ld~Q~qkSW0nWqYqQ;9Z(IFU z^;ZD!gL&QA?#53XU-UVdTTv!(Ion;eIfFOTcGUi{EwAZ3HqObuw>|yvRd?o|_EfK!~9y+7CTwKbN znQn?0j=IwM_`=6WA`J-jrSl%5T(BCivuqH)i3g6)P5_KFn7a=C4_^ zdf9@tYZhOjt)_d(hn7uQhd?#J-F9cYEzcV)e0a4OH(8(pc^o=?YxzKYqY+W#PzL#y z5D}+gC&RBegFc0ePmwweNM;u5L|hS z(3i4YC=Wx%oV*KxGmd?BJ@1by05m%FF0*)Uy!H|TC{M$+oEL-Urn?;J;?-u-1)58j z<1md2)Z>}wDI(%j-WvE_dDl4d*iVs<`M436E024phJvO}!`S7!@@{s@#k?aBPI(-M zX&j(lE##$$h|{o7!0*bt&B-6{Tl4oYaK=gFqHBOWo|C4ID6g*yRQfVB*v`gb&0s)K zqw;a@Sux`+Jgl zDuHc_<0!r_)v;Xj*FNUIEpuNQDEN>7`@9G`2R;V}JhvYSbD@6(aD>3Lz=;CcM=|_# zfzt%e6v#e;;qwJ97szg!^bZKUP9Wz_NawRbeU{G>;Ua+mp?-^^!;s30U@`|odc$Y# z3x5h(K8)|2$!5L>-JO1W;)#=rUYnL;BS+KZ`vbFJxo<(cYtwjUxm}x9zSqlyYtw3F zZQA>twP_c0vNmn|8@M(N-$ZUfQy8h=+C_0;2odh%?sSb4*462nrOwWNH}VR;yUX+6 zxju#K)Ozq+(5XyYpVXH2OP#acr(*9q*RJubb*^2DHskxj-j=?2ipzN3H`l2&czyRa zcpLWgN!;CDj`dQ7Yt-(;`kun`ALhDeVPK6Kcyephdg8m#ke@s6-EYpcMNmhDd1=ME zr)sRJdk$-4ct*q&S(i2y-rt(a&Jp} z?!5Kp+E%u4p54BYYrtv`e(jzqD>-l7J|}j~71vaiqwR7%Ul4C<>4S7Pqbv%~nb=*h zmhR$%SWAcXt%vl?dGytWj1{_v+-yh-0}`B+eqy)_8TH>Pjp z^jT_K){i$G6T$bTkBMipF|V%6Gw1o-n=|LV<}AN69m#flE?;?de_2Jtg;}red)X6l z{(K^Zb#>$Ix;m`AvuzaX1R)g@2{uCR}V%>Ij73YDe)PoWq6+8>W!L7TzUiOgh55n8aE_NbX5iWM(y14M1Z~y#?@T6~lRz-N$w|`+pc-pr=yCOXA z+n-Yrp7`z0s|e5hj@;U(B6sXJR?=03Cx81FRoJt?nH{;8-Xm-S@DkjVE@u_^(<_{V zz_D7b_wC@KwwD;mBC^Yz{JDuo9p9Y9gO2Zl#Ql!1F7b-vo0a&h@mcS$ zAuVdWl_p1a&%e!&#hIUCyY*yPN#!U+m95NowYcqS`wMje<@dM1#{LrMSkQ(MuWyYkjKqmE$DT)lztm!g^X&C1mU3qcH)BDx=Y?jjw zTpEWWJ_~ufh=|j$Z^2KT?Tc;|<~QAQcNoU&`vL)s<9CYP@>6+FBNT1EQP{krk-4MP z1E_q=t3DeHeh>IsiHK7^LTxJyN*8$+!VVaO*w)mkJoPJd>D|xYyE$h5wbfW@9p~%w zANm7*e|R5W#@4yHp?nN>9rspVN5#I9^Z)O0@{20<&M5Hb+XIF!g2~-1k~Ema)l$F)zr2vPB4#6Lm&rwBCXA0Yf<@n0tJYJqnWLe9P7 z|Cacl5PvHnu3riKwb0G^;*cAlewcoxz@r5oFYsJK$e$_xdV!Y-{D8pC0(k@`<$akD z@qQxke}(>{z?TKaQ2*rbD{zRwT0-P?ia>r$j(l?jE*8kWY^1M7dnG>|3?A0u9gU{u zMeq*h`;Fo3VLChSU_s;j1%DHl^_sCrz@P-Jf?*ZTqX7O{t$(+5%`ZxIMiwd+X`PWba4~%~oUXGiaQXjW+GdHu>xK>^tR~d$`W(^~CKs zujwqn>UmuGnPv`<3$OxNfYsAZ7Ge>cnJg4vt?`TzG4n zj+(jx-A8cw6Y&?}P|p7*|3>-5=_cdyFAnB={&12?=WL74-4+cn`82}#XK#z1zbzUa zO16<3_&=j$48dpPceOuHk4W>&3L4*4U5zEn@I0+mVSp;H za4h)% zY|3pU{Q8b#cx^#=?(I5se&=sEY$-J#i8;W_%1-XulYseb3_@kq5hg{?gx<$I9&k+- z0*_=k;Bf-S6QWU^DDYyTzhB^o1b$TDorK`ON8pn}XFI3dc7Y9q;A1``sHkf3*NC6( zo#6-4kH@OIo3gH3{rFwQuesWda)Qh0$9++o(d_Zp+Lkx_AMZJX zd)}3RK6@KA&OZhmhV!|kKf=G^zCK{1>Akq55i7U-;|#+LuQVFnN-{3bA7fa_HjUnk zV-FG>&-i|wjd#3>H{FZU>h90s-wFP30LIu(uOv`o80)a3ehI1XHcVt-&QE!hFIl^C;T2=@we#crF=Kyv-ja>u$HpJI zVQl;hRz>fiqw_gb$8wrd5xs?FS1v@-Qn&_*pQXZUN=2Vr{7sP?g6pFjOW?1Ta2CiE z6P~!mzaescurX>3Xtt{VfRM4ca+61^7tdc*V?GX;BVDz6*_y>QMiI%;$TrDi4dqCN zZ4xLpZ_<>EpMTn%i08+td2PCT$I{M;wYkfAC1(e(%`ck2hA#k_A+tpj4i&go<;_>^ zU*~3xw}M)4^mVprzEkMV9V ze(K>G2dKxnR;C)WcnQ>maWIG<6zw83?Yto1Odt&B@yU zT;ov2vyhh}B2L3T48JSyHVEOHhWUe;B`5D=z!@ivi;i=IKd`9zXBe-$1sWp$9>)#Z z9?4QP-Os=a5sk|CH}DmsuXDccD;fz<I`nj}ue&*6dTxsJGdH2@Xlsd?@((%O)EVEcG5T5ap$kmSxqg;F=sMHi z`$;gLPwL~t0Ewr>e*z%ial9Bl1yJ8>^m`rO1x9nv>$r3MxV+DD{?6YPM&Ct!mm{rW z^z;$r12gkVSWAeiU|tCu2tmJ0{Cws}r+P*D9RlwY`lkhcL+IZXKifUy{aEPl7eCvY ztG|yHKc5-$PZocz_~(hgLHy=^@u!gOmGRb^JtfCu($P+Wcc!n})le|Gw*Z zB!VVgV}RXL;ib2>b6-FJ9p6iBNJOAxQ&-=g@LN4^dfWbV(25pZhW_E}QTWmNe&Bm{ zpK))j^F`ckk;ckskR5!!Qadp>Dg_6-M@ce zWO$}!QuEgKV*pE#A3uwI5g0Qal}=`(>AP_3Ra0v;eO=$)UR_%>ab4@n`}hAWQkF#h zW!fe+-%#=bWyyV_Z4ca73mLUjT6@X8`QDngUQL;nO@p^&CnfG^XPbzoZ+)IT42!1M zKaczjWexhwE^5LZ|-Fz36_3xp4{X|>DZ>kEsrmPt& zo_G9SFFoCSr zmI3bR*YUdRQT`XTcn!8(uv{MP(<{p&3^Ng}rQQWCxR;S-+?&%lrS(PJ1M>^oiyp%K z%*Iya^Crp;yv{&gUo2`mW<1q*GdtW!WUQKz633cQ(^fg=YXW5^Y{B z@|d{0FP^`$D8fop*spRG<_S|+)T6ywB__^kzoj9I#F-qm(W(f zjP`-&abxQj7$19J>eAP@>$5x!@$sB~{j&&50O$S0(ROs)+faLNdqeu(_8`3|`$Q%4 zw59!LsGlJiJNHgkwN6dnTT+7bn0_sAws%-l+Sn#*5HEqW_JY3$8`&;%z(>5P5D^FB z*=@sXrK~o6N2htkx!WeM&@0PWZl)cbZs#)CR{1PbCZ5X|quvenK2TPZ)3>(Qrmt&{ zX0K~K=a(q6MxV)D&4Z2ECWhV}>MU4cGsTH~$leiPf$$Dbd~ zw#w&hEBdefKOZdJz6TDvficFJUH?5_6X|9zkCzc z5@5e2=-a@<_|O+kc|a$}`}E_epDWssRvGW#2i^zwUYh3f!FR@{){hmG1NM5hK8kmc zwkzhn&yFo6D2K8>lK(S!FUZ#iktXl67wYnpkds1tVLg5o_dgsso~f2c`Tna#T_eBI z)TY*SdUJbCYIFPWy3OsQYd7N^^KtZ{Gh3$M_#U6_6Zi9=BlmhoZi^%L7r@!SMbMtu z-dLB+%bnmSL>ZelhQ8=UhLg4$VHFPVN8x`eb#FVv$nz2SW5@%)Jx$rahM(n+{I^VM z+}zG~lEM4REgQtGjHWV`h+p+PCdwWCz4?$3o| zuU&_5wlj_YtAg;3^WFV&Ye_J&bY{H6dGHdj#82w^ZmciCU?qyZX+3+=iy4yPh&%{7 zZQR*z2YQcQIiZuv&t44+Uj!Y8 z_?0CH!hqEJiB{PO_dZ5&iOp4bwtU`nd%u!gdfbWEUpD5+s+sBo&F~LEvgd?eRD<2~qDan@DwL3@Uq9Ah`=KZ?*vW|J$BtqyW5a+4u}>gIJjSEK5?+KBWB<%AfHQI*QuC)x zAZoTm&UyuC{9>d??_7UYkE@u-#g{`$WQf-TBVe4SWw(8!g{$N48gUQY_><1g(|!zc?# zV!{h4e4+J4d!)&}$ok?vW{`8S^_BPFvHboL>q~e&d2C#@zpDHS9GbP7Pq%{jrEm#y`%h$Vz28UhL_1i%nNwln;%bn0wllke#ovQ3T{p! z_m1+oyV9o^=wBZ*o0VcOFp=0W1V>^6Pzphe8Tdmi!92>HnIschDr~9j0b?esh>eZ`P@7}6+`NuHbM*0}^qkj$( z>Di>C0_cDcJCy+OH!dQ-qD zD!ro)R1rD7DZP$5YBu z%BzlYDbDSn;IvQiSLm)a!dC0X#?7ULtr20$JrU({*S-PPlrS0|6IU26uTNdd;9i?1 z1I_4OVkmSmGPxT@fPE}CEaZm4&R!=Qp>$a5!60(1{-nEyflk$N-K%g%zN?8EHEt&o zIWMzP+Z^^arfAiv#y`&D-*XuPovQKQLYRB6?)QtwZzgdc%ivUx$DC8S_v`*$GyZfo z)&uMUPR)c1JG+$oAag)ZV0(@YqVR=aaSrGrGNYR)<$Rr)^QmM;`Oc^8VNUgN7uK~Z z=MTEV*QBd)mqUbeE3R;xF(^{sRBhU*MPh1%6c*c;Zix&wRFK zisMjzki|Yfi;aQ@gvlgJ7c|*1B|To3G+EaSX1i+Ae?r82sJ;7bhpDTygkyE9T%+5) zN{xG#S+ukLf1|5Ire2V?Iv?nqmYVkel~m`KiKp5%U_8P1zB z2W54CYl?APi*kRb+o1~POrN4we_*y}IXWlS=z3kNg*qo#g~C~C8A^2>OT`;#R}!lhTpC48QD_hBeB4(xj=ugs>Eu z6;bNHIu0{=-IvjuCZpQpb!U3hA+NI}cqTJ?OBc;ydves7iWeg_idFyE6q<6l|F|vm zM)s*!KtoOlrPH+}m`L~X~PnMf}ekb`%o&(NcBQNCvN6&|r1utQK ze=rP7P98(Sf7BG_i>G2X>by>d8LkdvVajl&Y=KU3r4@C7rn|aw%YskxV&=FUN8D?a zDMPONU1aXms%hNm&G|mde72EFiEDgPml~;6S-N7&TwSrnS-N7&TwSqh(*(;+jx}o9 zI~2N)Rc|&zDKNmkD;K=U2(2{8LEIL|)51<&M1T;B8; z?y`GUW3+$Fy_g;0nsGr+Mcf~{dZ<`t?)1l9eP1rgZjIsY+is|(!TszDKa#lMzwr7# zAyQL<|IDlYq$V*WT|?F#5*avQJ!-KW*$1~VvtJq!RY6V&-FFOCJGmEBHb$qjJe~VF zC;t;#cPf;k#)Pl(>DPd|6bn(xXv>@#$15wJ$>O8n+xzA zPhWs)HLaUsxFNQCyAeYXFZsmWnjoexLCkG&F^^Ns3oOmuMhr#l^NIO>f|xrK#N1`Y z%(#gKxrHO8x;})h6uhH0co&wG*Oy@@hSX3b;CQEonr{swzOs?g85_Kwl8TjONN1v_ zgi4wad&T-U!o`u?NX&anH8z%eSs%_U$}R9_Y>3o(Phy{kaBg067<)CedXbQ~IOJuP zct;d8BmgGgkk?^dUr-Z6<^K--bO?|n!k;sg2MOh># zT%HHdDlaXBl7v`_$fle!FFn+p6Pa0Ao|`i}FFYNUDy#4=Dk<5!)JA|U$ zrUOdanr3<3jW~{IhIdqBX>mAQP>+o;D!t55X=#pbytGDeGtE&UuPfw@3VC0rnCX#O zxyX{0Ez1q(B78bBf)X-bxt z+UQM1`6^p)V|%kSd)IC7aA4e7C8d$va8B4et)y)61>xM%yrwsED!p+f-qezmlhI<~ z8EAfQL1W6vr4Z%)Ty5Ct-Mb#`Sy)nVj>_wSCtt-{_1$Gw_4`zE$BU{j!%lUvf9fRYZ9d6VkpEOFbt*}a@sF`m<- zZ4bD;oRDn0+v(+eVcT7*m(Tj>CbyT5*n7@Q@8v_0>G>REJRd%!`8=Tv^XF_2{*45ZL{BrNIz@M{G(i z%a|4<101i-HeiAdXjR7Z9F=UndEIg}PoRqRW1{as%y8lCL;mn0v@Poj zt5Q6M_!1jkVzxqET3r)8E7q;}*60tbK^Why{9#q?9f~tuW5Xo^s_R<1Vnu7}V;$Ws z?QM!P&xH7e?JxstFwBZG$b{JHSXa-gnBpXGnDA(OXS6!j-Q3x}T5(cm3$vhoRYz;A zJEk~cvw?*$KkN3DgwwCM3RB{~ubdeS8!GqUpHSC{!K&j@*1}fg|Lo_B6s4~ z3MSqR>G(C61e#?B;wT^>aW>sPh-<)Q*@WM<_;usA20!cep`#qYJJNPE6g$JkxD4DV z`842i{H(hQJp1sQ3Z5!oxvm1w`{Y9y8xpPpYygl+8j6^KTQ ziff~=FYH{a8r!pHt}MbN7jL>E@gQ=PGn&R7?s>vZqv=!}BwX$6nAi?n@6_X7Xb z?$H-4))}p@t8b2}uI~1ZXk#0&4p+T1*3{O{|N7Rtt}Yd8>FzLj7Dfvzm&`9KN|c_^ z16Qua=IGJZSR;#By&zg#QBymwtSq{qvZA(Vc`c-N_jI;J>lzwR&@Qvf{lQz^ud3@t zu^MEe6YXe*-D7^VQFnD){RNfPMXI~GrHch$RoC@3g|&;G7>jCpBW}C1s^ipxcC=x8 zYjl2Vd;Ql;*%r;KC@d?gjxMZRQc;M#dsojcP`ag~^)Rqhv1ZiUtUfNu4uTCW zUG<$Ut6JLXy4yR4w2K+2bk|1dtL807YTeD3m6tDH;3sgY7=!CJuehwJ5M8P()_GwJ zTMF9LWtH=4ReNJ&7cP;FEr|POsk(Z7x7doZc-66{1-iG3?25|TXmNQ}Sy6crIxe%U zK|iTpP!u%{tgvi~dkvTO^K13vE;YBEzC&1xCFsg=!(V-n1a6QP77`1G`sUO##&NbQHy^6g{+rN$JeTv_2@fo z+B86J*7~~ER@^qRH65|~?pQ;#uCW`#P5eI6mv-sGs(7K6wRbis88`IRtt>?xMi8`m zQSn(7(W2!GimG&{UodaUS&M2pSfL-96EhO(Dfgo(NIZiYj&Tdy;@u!$t*qAJ7 zy4ih=GBaQG)US+>d?+isWKA^T=Hxi0coQcM1-!yIHv-S=u2@}XeX}0K%?++iI?yR` z2gIUn?QLL$NB5+zSZgE9Bci6~&df7S$_v-Cdd~2-a73-d=!|12Vx4WKxj4`kS1gJ5 zQ+wCO+OQ!DiodkDx^~IDvV?w=Fvf7mmysskO$TrFYV&w8Ll&-XH%H%=wrFQvTN9om z81))-KOAbr=2OLv*+c~RY#r1BAb?2Mzp0Bg9clS(% z4;K^^PlMqZqkm^hQ*$>SoH7_7c@&Q``#d#CFha|SSG1@$x(J3vFza89?kLYO))Fk} zin>dhwwPyzkiWh84ee+COcR{H)_Ezq4|FN9+W*T~;A1%pQzOpmFj>~F0RR3ep`9pB zM7VFJrUG9wdj|16gXzhKnVULMXuJ|>7ql-GLX($n27cr}93S81Y#MkK*zY5a6%J+o;F;n4Yp8Yy(@5YWQs$cvTtZ&d1xr`r<0!_se@`o~ak* zv3^z_zoSx~EqV(Z-sfyys}Kf%d_T8V@bTTx#@p-Bf-rx+FU^CFH^P`NXTmn$i{ZCv z;Z<2d)WsG8w3W98e!sj;XwVf%NO^2VD{l+@etFH4l`8fHSnm}>#V$+5#TT1KFG^N7|X{moVI-awnDIa4f6Ww5ohH+0h)gHFS-Y(qMgwX ztYaL1F9_Q3x}4!{j(!FP57*7|eFuD#ECICnvaH~XyGOt`5T^$D$nU#M>fSu1cA%2u z5l$Nv)|d5XoA~9m&Q|I=UqYKN*bXXTML&d9&Xl8}#P(I0{R{QG_Jh42Sa z41YXn7hruP&j+oFbBk?V#B{o4x9T3wzR52d}blLo>N7u{E*!9<0b=yUaCp z(eAY!v8Y}elYk-b*b=%j)`S-_yI777^!|XM>AA+zpz@y<&+M z3-$_5MOYR-JFg%j-5Q}ew!2?}brKOb*$U$6SY{<+0?nONooX3fQpDa3hr zwqPtLQm2miF%j`u7<04cqr8|nWGymdu3L;}7sd*=1Zs7}QZpBJAtN32JEVOqR+aUa zbl_z20doZx3N990E*KTOKya-fpLa~ZS@2te*9cxG$ax_7?ht%L@JE9GBFK3g`CbwH zh2YzQ?+Jb=$d7E~=b3y&j?css1!oG*6RZ@}$B}@qMQD!0Ot(RBtKcny_X_?{@Rx#r z5ag0O)9d{}f%Amc`+Q*7xBjMD#WjH@!5WgkiypJgNdxAd@d{R&=u^|4}q>=8w z1b;8_Nx1Kr{v^RXBKT$ro+jbNf=dPK1v><{2vVh&>Axe`C*fL=4f(t(^iGNYH^EON zT;cv_dd}U5X+)$OC#W?{LFWryLBzX9jnH+1-x9n*;_nsuMZq_SkpGt8uO&PUFMZ7K zI3nUF3Qm^rJi#*s%LUIB>=E23c$?sTM96zk@KFhWPVjZX0m1(wg8vi242*{?*Re!| z!$3=8wuGN9xKwbZ#5W4AlJIW`ULkm$;P(U{BBDLHmmBkYPVjZX_XIy8q8z^y{G)`Y z z=erFtC^$_pB8aJrj-M;INU%zf&wr+0A=oU~A-G1cSMYMdt%BDJ_6go8NL>OqWXi{S zOci<>5j58`8PD>OE)$yNB7L6FD}?SAn&n~qRYJ2Iq_+wEu+Te%en#l`h2AH03ZLu9 zKb?s5vze#$PUUpU`&*&3zphzg_6(g?>rsexdgWt@j@X zzn*76eh@#V=RB18%@O)Eq4C%lcHiud&fKn^jtAgZ6 z>@Bf3HTcxtK=A+UabYv*!5$O)SL{ten1cOtFuiY=0;o18w*glGcL1?( z%*fw^_f_~)P}bz28gO8D?Vh{=Hs;6=Hlsg@EQs3;!{$}n_h-^h_$*^5eC7_Mp$+d- z`1NiXI0|)P+CbG+1FF}YKRH*8*^RR<$KsddMPivrk&PH6`*t&)_TS`ZI?ADqGV6Ai zuWrxNZdaX6duxpNeY;_A`@yKW3i|`p?mkM@j>et=qn)ZgZzswJ`&VuIo&;HxtNKhC z^nL)6594gO-p?V@kG(d$fzLvHyQ`|4OtK3+7+WtKLDNx zd(YQvV--L|kkY*mz{1{jc_^^78%()T546*6+6J>u_WJM| z>volE%l0?}GJ4TwOz&3Fz8UuTupwR_3=AAIYhyooc%2KtmyA72$VdK9P=@p!;EMzX zMn8L1Kl8cdm8-P;cz!#!>}5Y$BW*tgGO(JZ^EwxG+K)beHv31gZ`ZZLGXXrPx8`BJ z_Cr4Fg*xl&cMRg$w^2>d|q7;spEYF^PuI*uut|Z_`xy(AL$M2>6VO6GYncO2v|S zf;EGlXpNS$d?^#`lxbB>t88mkJ!qfI!kiGVK|-RT*46QCSrfR_YXLl8`Ir-H=JX^@ zs73_hkOJ3+&mIS;8|&3rQx8*66H44gr0<^^~G zSIOFHIGGF4Ny*3He?;=r3{Jie=~9v(M+mH$Q^0DhncV}3b!s1D9@u(Gk)$c|)6C|4 zeU=G~7FQz1`6^d zsKEWmXH=jF;c4H+A5DL+C)4+A)?Bc0zRx&j&1JMf)8Cnl^O^qYG}BqS`&J^sr~t41 zF@e#af%pwm8e>iP^!GF8{4sZv`BN@RIaOnBAd47ljy1-bS|ThUM@NvAUL&n^zADK% z<%UVBwic>jytDYYmCCJRJo3H^LdS8-NoPK2x6v5Kz{}5FlC>MtSuZt0Vp_ zo0zjxJ3a>j+-6!PIcJZhW{cY*WtwmoqUgr|9V+4W>I6&FgdGST$6#$-{bQYUiJG_{ zOxVRvjsFOL?me2j8f&W|mPW6(ooTG=Bx;gGrqIo%(0H@b2GRdBjCp+TDT&5BpE`~) z)cM&unwZdhL>rOh<(1{;=D~I&H#`g0FZm^v{^(DSbQ7 zdvRSOtUhAl(l882!rn_NLC{%-(B+eM$HN?@J|RuueGv1(>gWCz{}+H!%3^btT)qr<@;F=i@lp zH6^okiD3(K6f9;U;Z>no}4Cin5S)FVO^hVGjX#2q%bKCu%wjH6Z2|FLd*|XFp z#fgf>bb#OXV+?5PC~K%~2gCRtK2?~Q&mP=ug&Dy@+Fe}fUg2FHxGd??5u1}Y;ibsR z;~iB7UcHBQZN@m}OUH77EzZaCWq}4?A?+7zdX_iG7yeE7(dXfQftcASf%)t-LH9e;%&(7=fV_@8j|}-{G|02lwnwP*n9v`#hTJS}{GLo=}D5S3;?!PmNKlEhcEJ z9;{BTwK$~*u_AE|*}OlcLmgN5{d~&I3H6sumn@i%Gk)4vK^4|$h1!Z}o#4YL0XJiT zCzv4B>Vyuf;Hy=7KflVh*@&HLxQElX^|$XZsS{YBv{?8o#rnoS$)~438U2!>7}Nlq7if+>ZS5tWPMqGwuxWfX;>}htDf2ZExrXlgy4x#S=ZRhdi=R$;? zuakZmH0#TGI%$7L!hW0048QPB6 zKt%k##N&@pzYF&^Sd|N$ESM|!6~V=V><8qF3SJ4hE8{#Ajeb6 zxkK;)L5_jmJ6OMsP(vzZne-i3Tk~Vgufs( zU#}?tkAnJpDrmhIA@EFG2gd7d27zmZzD)4%1@9JoO7JBjey<90i$BW2M@fwVTvyWE z)Q^0l1*sd(@Uw*08)qS&J1{bQjo@{}>Ze+u6aKxh<)xA&XO3l^?lWG^ZVTMK09seK7Q}cPV3dE`VoIZF3w57_ipg=%mIGu zn%jT#-bMJ9Mc%&}Pvqdd=3$PawB@yOINuoIrZ^jbu3ERRH(q)JPNHxeKE-e`;Q~4hecSQ-LJKhd5*$K6`^pQ#F&tzb zZmO@(#p2$qug}C9CJuGUg$-;hmr=Pbv**mt?WoUfZE5RSlM6LyGHd(lc{RcOP~Pm& zm-3X;>m02zM{p}%FJ)rNi7C{lv<|nGt(6VZwTcs=uC=RRhpR%cPBhqTeZv}7(o_=G zwVmkm*w;XX22U?TA!B~JrLD2unm*R8T-h1B&>veTFQD;)9NvRRp}w5F59tc<;}xB)zngWfOqBh2q?HyHYUc__XJ661F5urkPp;Uw;?yh4On zKO@=SE{$^y#?3`o!g~q`mYelp8`yf(!ta-NC5XonW}S_zLV#c17!W~(Q6A-5c`M+z zY2a1-A~jq+I*0JB0AG*^#8D<)8-D(L3qWKdjQRTCFNeO*^g`Yx;IYn@iD>Rr6GQ#q zZ3x6`S6m+DGfy*o8omO&$J1uUI-l1DY4nGGk@uPThm^+h*j6!22fv@#-fiQ3H#36x z{!%3Jd2e%mefALgYoHlN$M{XashTo0zwPl^$uP!0Byj|`+bj6_>SyFC7&SlxUfVt5+lW1bK%FU0#&(RjKGgJ(&rxG z4&9Bh5Gy)7`(g0>v`mEKoEgni1^)r)X+fXt1L{XvzGsKfPYBIA{|bJ!19MB%+ljoo zrx$0+tcMQZ=%=sRl?)w7$ey5f7#&R3SM~Sp&e#Fo;H!JqN8Ev4bx)V44p0V zo`AERaH+4Hi*TGxG#YV7tA3tsWX_M?kMnRo>s13)YB}lyy*#zUb333fw+-bDZVm7( zC+G$4^7{IAsg6D;i29F0o263^5a+9%uG9w~V?4PEncRUN{fmH)4*6{d{kkvS3GT$X zCOh}K3crGlySz-$b8wbT&c^1Ls^T zoO8`{T0FEj^hmWH9_REovoG?j9JIy1@tQ!M!}5|B>EA#YuNTWfy|)|TSHk}a;^OsV z-Ec0=0_a^bEp$dd=*KxspH(?pCpQV{-4$qGZbI|OjU-l!u|I)qAj@S2mhHFnbfyku;fgp4pAve%}X%cz= zop~Xx6MTKonBa4J$3))PtM7xVEj-`Iqh4wNY11Q@CIzatxB;YDjx_pAwn%S3!uRU* zk=OT-cWmUjz2v9fCV3d=fiDw${&UcDf5ScIpbiD>TPsjs)Ct3qKKF{U*si*cs?WpO zEu#y3*Vtv5sPh;^I`FZ6mLF%!jD)V{E}Zu>!0;a+UY`Xs*R&@t*srFcY>wKoo9F59 z9nR^Pi!=JR?0x66&u+jOJ_DIsw+v8^IR*Np=&m1-pS;lFy@73*i*tq2BD@BD)K&cz zX$^+ddtB@I_hgmC?ZFMsz`-@eqW9LHn6b27>-2K#%MrAYYXh>1_ zVFG{-4QF1*FUROgcN7Ahhj4ql=71RQNd!Q_^4AQXF%2An{rGcovgzeC*vZwQ@F^!m zuc%&z;q`uiKd4@6lgMQ6Tnb4UaX(pcdd&OCMkW6@1Cwh|sHEh{_#ctX1<&N~WBwvOUPV%kba+A40=_Mb)e@b#IVl!2MYllwp zW8e&M`O_IZGKM7eH1&x#@grkmWQrddgQpqvH0_ZwD_Dm3kuhnc;z!1CZFBI*m;ev4 zkRxNXH_*p`rRPFk{K%N+nbk@LK|3!9Z5OKYhdYZhp&RUzrEEXz$WXx*T{~IPQ zB~@4LVnZGB$e8n4MtfuoyTrjq#+U&X-1O_tRi3d zAxIi?9s;>K>pbr;2Rcroo%6?Xp5y%Zdu)X&HD(j|Xx;0)tog{pMO4f8Q_J^b@Njh= z%A&km$g{}D_o?9X7j)LSfebe+ZM{A$BjgB-eu-Eaf4~s==9@sd6FF?V4N0U z&e*`&BXZ&fbL5E|W4JDTC4O$RE-fYS(+WzP%g`paf>Wcb=r-NMD%Pkm*CFaH_}xn# zHYgMDWDDo~3D79r%pj*~0&{XVlhs)~k<&o;GTn))v&K{M<&-=THb~07f~9fJL&MWE z<`YCy)7rgBOQ&Rh-7(T{^h>{ytXBHXTKaiL`YnUfZ`IPj3h7^_bRM#?fu(s&OQ+=h ztd>;x5nbWsV>h#mkMKe}XR}t1>U@`(T0Kf3&N;{52;hj6;y!LvKDR#zQImS|u{%F| z&ZM3K?99({P3oDW+$S69>q6`fZv=gaeJWdrA@*s^d7#SQqughYhR-?IG|qEX2z9DY zcpiDt9g7olUesY#D!2!McD8-2KAGe#b7cc6+s?90_y8+Um07ey5&peRq?5e+Q2&&q zQGw%-<><2@ZgLxZ<8TSxiH>d|whq6ZIp^z+<&JlBH>et?JIHuPH`tQ#Xrl?5-&v|V z87SRK%hVX|lYb*5cm_;iA4k_V#!2?^KPQLa`m1d#TC$>n87sI>Yr8 zhLad3zQRPkrl%c?Vi|93RtuWK5EE}hyLp$eC2=Ilu`OWIgtKgF856&ih3vEMM1B!? zChlj(PZ;ho6R%}Sh02`xWl9n%ed3#pd(u=kg%|4p#F^a_l>UbJg;((tHuCpx?B5X! z`N{fFJ=U$@l2BU|=EsgxIKjrngeyzS@~3m}{zy1B8=A-{X}OovNDbjo6%J(xJ?(vOJv5v*R8(w)9&=ua zR~hp1LS7a3zsKPMohiXvZ@smA<8ET4oY>|W4-493o|mWb}n--^)R^FHB`36Gd9j_6puaQ2lHSX-DL78%UFK!w(2J%t?sAl zK@0+~V8p<(VtswIBi4zHJ?mOqy4OZ8WaZ7WtzivlntyiYtNqAkD}Hz4cQ1a|zsj%K_ZRz!a;IgpjuzMdZNw(b*tWTS74}1KX=_rg z9eRIRY!!;VM|D6~LtV64?+zN(AA5{Ou-Si>v-57fKTn)r>#Yvm{`wG#z(#+!RkW_7 zU7~g!)oQ0bY_z5}Y=>>@Tf^N+}`tep1DVFS4($nu$D)n zFwAXa{rgWJv@LOb+iBE@Z5QRycY{@DV|Ah-qg}1Bm3jGdd~abO3??GYPfBZhrKQB1;|%= zKV(bJ(v?Y2B)0`d_p8MYxz(}O`K&a17LG>hsO#*CMOz2&8M!@l!yHbB8ZzQrbbp~8 zi?tfNzDaRG%sq=uvE@d#1?Qb&w)Qn!`ucUP%>_!_Aljxwg=r6AD{N|ns$<_~b068R z5Wi$Th5SQ&9`EX`_k9_sHymH*=RWFY^I^0LMhci~{I72J-(I)d|6sC@tBH6L`M>pE zoUPOjgz-7c=M?!cXH+K&jYp?;+c5?8JNrzVfk5&fj*pK|n}%{I@85j^);oU)AJ0?d zQ`tJpcLoCd^*C)ZR%1|j)&mdlcs;7&w`t&2C8O{y-xpxL6^Qf8yY?hgFUn*6Y`c6F zex@;#0O*uug77&jkFRd<$922Ox7>tjZhJjiK>72%3Hh!-81uz9qIkX+!*6B7tES;& z{As=bD{l?r{PGS!-Zq3$-ck73dTfE;ms~aA0~N2ObvEt_1o-9Mi^&q(gYwXnae24F zZ_~i5{tohj^oX-@T$=OCdl>TAgp^190$VE;PSx{c z7M1m9o7j5@UX>YC>H=FR&^|AtZje+4{z7vnpNjVEp6{}G-YKAP>rwKU@#br(GLUB;#-ZR5AYW8Y_NJZT$G zd3>L-wDl4H7P{&$`!Zy|eSHZ1t0DBKLul%vSjY5Su($7L)O9&^NPNi<+U{A)6^27h zZ>A>fSnHqZ`4af77tJi4lcIy?Lwa6nXSJFbd;`FI_fYd_?0-AVjP~FerrG56i*8^m z?~6nA--mm5P0+bNNPYegdh#6Sv!t%BD3tpc^4K2Yzs z3fv%kTLtxgs|e@92J_MS`#`_X@rw z_`1Y%ot6CWNcgV>N1^|*d=rR>=fW`YL-v+@eB>XnP?Sj7$>?cC*9>HHp`0oXiaF4S*#|Q=m=Ljwk zJYTS$2zkwdt%6^d`1OKY1aA~v&HD}M=xFDl9R`mSp*hYloZ|~ID2Sm-(;QzIK1Xn_ z;3B~)K|M}o8#&AmL!5F!Gd<}-p_z{Kc|xxcx?5<@e;9w2&|8Jp;}PWb3H`9dKPvRw zLiY>(d!Y{qJsP?rM=GFS09?hWgN!Qpm8DCsp9Z<`*x1@$*X>)3Oo-HLUY~yz^g za79>$CuDHs%RjKGz$#EaCT4K~x^z+%<3D)wgNF7VE^dF}1tP9!h`|b9%93CJacXU0 zc`-~^U^H<^3*!Ozwea?96Y`&JZ=np^qd%eFTtf#&D zM%v-e7Q%zg=MJ`k@LyTVAE}M%jF;|WM3m)Bv>X`BY2Kr zonW(|9(0lJLZQuPf|d2FI?NO2aQf!?b0P&t`n+joXU63<_un*}TEii{dN*{ep*N{U zI+3^W+Va{S1$|3gg$Yiu+UQtACz7^&o?6jQ{FBcpzd~eSDzxs5&Sa8LGcRGs!x4$f zgUN}ig(+#y5)3b?iCZj=q+Vk&3ZG^)8`a0Chv`Q~q0f#mvk~sx%t$_r0!jKY=>B3R zLfFZv+m{D$k^_8Xf|5Wq0UnHj*9a+rO8{H+?LalME`JSD%d4Pm&Yt-iIVA?Cl&&^%q;4GpnIs9NX? zCCU~3X$}+<3TBj+m4&CfqGZ5v*RKyi+B!C-z87@>XLXn1H+a|RU=EJb9W>29+a>6y zK=S0^=MRrR`wu&H>1w$MCuEwhc7N^+##i{i_GIQF92#mSXMe334X=&NXn2SCXEa!1 z`OD2|4s9B0R)~DIqa8gf+v-*&+LKxfQ8_*HudrbeDnH!($lu-cXahbD6u3d+Pd<(? ze#bbvq4T3d@v&RmG)zanZ_^{r#toex{Ws!yM_Fh2zKj6>Q#61_=njO9!Ht5ytjSjv-2aCcO!l_4P~U_p&6t{oQ>mN7k+treC5;6 z55!X*uaQjyucA8MIC{j{xIWPIa}Gi`4}2qSBxrj1yGIb9T`3x3xcTM7h=qs_VN)By zH^~w}D|+bs@;2~2GYlX3&G2g?-$dp3or21)ye>#K?ELb5G}3sJME~1-eL)9>|1IX1 zyyk;0u~c5dm1R37av%P?23us%dHPGDpFT$0GBczAKYnN6rVuiJ<@W-;X3iNSdGtC4 zZc0Al4tY)&I-P!o0#6|ykg|z--55Au=wiVt!DWKHH_4BuyvA0+F2RcgFA>}#_#GlT dhn|!HpOEmEh$!@SiRZoRqCc?z|Ecwv{|7^Je? tx_thread_new_time_slice, new_time_slice); TX_EL_END_FILTER +#define TX_EL_THREAD_TERMINATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_TERMINATE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_SLEEP_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SLEEP, timer_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_SUSPEND_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SUSPEND, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_RELINQUISH_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_RELINQUISH); TX_EL_END_FILTER +#define TX_EL_THREAD_RESUME_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESUME, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PRIORITY_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PRIORITY_CHANGE, thread_ptr, thread_ptr -> tx_thread_priority, new_priority); TX_EL_END_FILTER +#define TX_EL_THREAD_PREEMPTION_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PREEMPTION_CHANGE, thread_ptr, thread_ptr -> tx_thread_preempt_threshold, new_threshold); TX_EL_END_FILTER +#define TX_EL_THREAD_WAIT_ABORT_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_WAIT_ABORT, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_THREAD_ENTRY_EXIT_NOTIFY, thread_ptr, thread_entry_exit_notify); TX_EL_END_FILTER +#define TX_EL_THREAD_RESET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_PERFORMANCE_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_STACK_ERROR_NOTIFY, stack_error_handler); TX_EL_END_FILTER +#define TX_EL_TIME_SET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_SET, new_time); TX_EL_END_FILTER +#define TX_EL_TIME_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_GET, _tx_timer_system_clock); TX_EL_END_FILTER +#define TX_EL_TIMER_DELETE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DELETE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_CREATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate); TX_EL_END_FILTER +#define TX_EL_TIMER_CHANGE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_TIMER_CHANGE, timer_ptr, initial_ticks, reschedule_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_IDENTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_IDENTIFY); TX_EL_END_FILTER +#define TX_EL_TIMER_DEACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DEACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_ACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_ACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_PERFORMANCE_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_GET, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_DELETE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_DELETE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CREATE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_CREATE, semaphore_ptr, initial_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PRIORITIZE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PRIORITIZE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CEILING_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_SEMAPHORE_CEILING_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, ceiling); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT_NOTIFY, semaphore_ptr, semaphore_put_notify); TX_EL_END_FILTER +#define TX_EL_QUEUE_FRONT_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_FRONT_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_RECEIVE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_RECEIVE, queue_ptr, destination_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_FLUSH_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_FLUSH, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_DELETE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_DELETE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_CREATE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_QUEUE_CREATE, queue_ptr, queue_start, queue_size, message_size); TX_EL_END_FILTER +#define TX_EL_QUEUE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PRIORITIZE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PRIORITIZE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PERFORMANCE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_NOTIFY_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND_NOTIFY, queue_ptr, queue_send_notify); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_GET, group_ptr, requested_flags, get_option); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_DELETE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_DELETE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_CREATE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_CREATE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_EVENT_FLAGS_SET_NOTIFY, group_ptr, events_set_notify); TX_EL_END_FILTER +#define TX_EL_BYTE_RELEASE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BYTE_RELEASE, pool_ptr, memory_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_DELETE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_CREATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_POOL_CREATE, pool_ptr, pool_start, pool_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PRIORITIZE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_ALLOCATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_ALLOCATE, pool_ptr, memory_ptr, memory_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_BLOCK_RELEASE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_RELEASE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_DELETE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_CREATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_BLOCK_POOL_CREATE, pool_ptr, pool_start, pool_size, block_size); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PRIORITIZE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_ALLOCATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_ALLOCATE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_MUTEX_CREATE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_MUTEX_CREATE, mutex_ptr, inherit); TX_EL_END_FILTER +#define TX_EL_MUTEX_DELETE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_DELETE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_GET, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PRIORITIZE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PRIORITIZE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PUT_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_PUT, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PERFORMANCE_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER + + +#endif + + +/* Define Event Log function prototypes. */ + +VOID _tx_el_initialize(VOID); +UINT _tx_el_thread_register(TX_THREAD *thread_ptr); +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr); +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4); +VOID _tx_el_thread_running(TX_THREAD *thread_ptr); +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr); +VOID _tx_el_interrupt(UINT interrupt_number); +VOID _tx_el_interrupt_end(UINT interrupt_number); +VOID _tx_el_interrupt_control_call(void); +VOID _tx_el_event_log_on(void); +VOID _tx_el_event_log_off(void); +VOID _tx_el_event_filter_set(UINT filter); + + +/* Define macros that are used inside the ThreadX source code. + If event logging is disabled, these macros will be defined + as white space. */ + +#ifdef TX_ENABLE_EVENT_LOGGING +#ifndef TX_NO_EVENT_INFO +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =\ + (ULONG) e;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#endif +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) +#define TX_EL_THREAD_REGISTER(a) +#define TX_EL_THREAD_UNREGISTER(a) +#define TX_EL_INITIALIZE +#endif + +#endif + diff --git a/ports/cortex_r5/ghs/inc/tx_ghs.h b/ports/cortex_r5/ghs/inc/tx_ghs.h new file mode 100644 index 00000000..ca976916 --- /dev/null +++ b/ports/cortex_r5/ghs/inc/tx_ghs.h @@ -0,0 +1,77 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#ifndef _TX_GHS_H_ +#define _TX_GHS_H_ + +#include +#include +#include +#include + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +extern void *__ghs_GetThreadLocalStorageItem(int specifier); + +/* Thread-local storage routines for Green Hills releases 5.x and beyond. + The following specifiers are used when calling + __ghs_GetThreadLocalStorageItem. + + If __ghs_GetThreadLocalStorageItem is customized to + return a per-thread errno value, define the preprocessor symbol + USE_THREAD_LOCAL_ERRNO in ind_errn.c. + */ + +enum __ghs_ThreadLocalStorage_specifier { + __ghs_TLS_asctime_buff, + __ghs_TLS_tmpnam_space, + __ghs_TLS_strtok_saved_pos, + __ghs_TLS_Errno, + __ghs_TLS_gmtime_temp, + __ghs_TLS___eh_globals, + __ghs_TLS_SignalHandlers +}; +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ +typedef void (*SignalHandler)(int); + +typedef struct +{ + int Errno; /* errno. */ + SignalHandler SignalHandlers[_SIGMAX]; /* signal() buffer. */ + char tmpnam_space[L_tmpnam]; /* tmpnam(NULL) buffer. */ + char asctime_buff[30]; /* . */ + char *strtok_saved_pos; /* strtok() position. */ + struct tm gmtime_temp; /* gmtime() and localtime() buffer. */ + void *__eh_globals; /* Pointer for C++ exception handling. */ +} ThreadLocalStorage; + +ThreadLocalStorage *GetThreadLocalStorage(void); +#endif + + +void __ghsLock(void); +void __ghsUnlock(void); + +int __ghs_SaveSignalContext(jmp_buf); +void __ghs_RestoreSignalContext(jmp_buf); + +/* prototypes for FILE lock routines. */ +void __ghs_flock_file(void *); +void __ghs_funlock_file(void *); +int __ghs_ftrylock_file(void *); +void __ghs_flock_create(void **); +void __ghs_flock_destroy(void *); + +/* prototype for GHS/ThreadX error shell checking. */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal); + +#endif /* _TX_GHS_H_ */ diff --git a/ports/cortex_r5/green/inc/tx_port.h b/ports/cortex_r5/ghs/inc/tx_port.h similarity index 91% rename from ports/cortex_r5/green/inc/tx_port.h rename to ports/cortex_r5/ghs/inc/tx_port.h index af56ba5e..4ffe1455 100644 --- a/ports/cortex_r5/green/inc/tx_port.h +++ b/ports/cortex_r5/ghs/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-R5/Green Hills */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-R5/GHS */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -78,7 +78,7 @@ #include "tx_ghs.h" -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -114,12 +114,12 @@ typedef unsigned short USHORT; #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #ifdef TX_ENABLE_FIQ_SUPPORT #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ @@ -134,13 +134,13 @@ typedef unsigned short USHORT; /* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ -#define TX_EL_TICKS_PER_SECOND 1000000 +#define TX_EL_TICKS_PER_SECOND 1000000 /* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply - simulate the time-stamp source with a counter. */ + simulate the time-stamp source with a counter. */ -#define read_tbu() _tx_el_time_base_upper -#define read_tbl() ++_tx_el_time_base_lower +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower /* Define the port specific options for the _tx_build_options variable. This variable indicates @@ -174,7 +174,7 @@ typedef unsigned short USHORT; #define TX_INLINE_INITIALIZATION -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -186,16 +186,16 @@ typedef unsigned short USHORT; /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 #define TX_THREAD_EXTENSION_2 ULONG tx_thread_vfp_enable; \ VOID *tx_thread_eh_globals; \ int Errno; /* errno. */ \ char *strtok_saved_pos; /* strtok() position. */ -#define TX_THREAD_EXTENSION_3 +#define TX_THREAD_EXTENSION_3 /* Define the port extensions of the remaining ThreadX objects. */ @@ -209,11 +209,11 @@ typedef unsigned short USHORT; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -243,7 +243,7 @@ typedef unsigned short USHORT; extern void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr); \ __tx_cpp_exception_cleanup(thread_ptr); \ } -#else +#else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) \ { \ #pragma weak __cpp_exception_cleanup \ @@ -281,18 +281,18 @@ typedef unsigned short USHORT; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) m = m & ((ULONG) (-((LONG) m))); \ b = __CLZ32(m); \ - b = 31 - b; + b = 31 - b; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -302,7 +302,7 @@ typedef unsigned short USHORT; unsigned int _tx_thread_interrupt_disable(void); void _tx_thread_interrupt_restore(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); @@ -310,7 +310,7 @@ void _tx_thread_interrupt_restore(unsigned int new_po #else -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER >= 350) @@ -349,7 +349,7 @@ asm int disable_ints(void) MSR CPSR_c,r1 #else #ifdef TX_ENABLE_FIQ_SUPPORT - CPSID if + CPSID if #else CPSID i #endif @@ -395,7 +395,7 @@ void tx_thread_vfp_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-R5/Green Hills Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-R5/Green Hills Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports/cortex_r5/green/readme_threadx.txt b/ports/cortex_r5/ghs/readme_threadx.txt similarity index 100% rename from ports/cortex_r5/green/readme_threadx.txt rename to ports/cortex_r5/ghs/readme_threadx.txt diff --git a/ports/cortex_r5/ghs/src/tx_el.c b/ports/cortex_r5/ghs/src/tx_el.c new file mode 100644 index 00000000..d8f056d7 --- /dev/null +++ b/ports/cortex_r5/ghs/src/tx_el.c @@ -0,0 +1,1165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_EL_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_el.h" +#include "string.h" + + +/* Define global variables used to manage the event pool. */ + +UCHAR *_tx_el_tni_start; +UCHAR **_tx_el_current_event; +UCHAR *_tx_el_event_area_start; +UCHAR *_tx_el_event_area_end; +UINT _tx_el_maximum_events; +ULONG _tx_el_total_events; +UINT _tx_el_event_filter; +ULONG _tx_el_time_base_upper; +ULONG _tx_el_time_base_lower; + +extern char __ghsbegin_eventlog[]; +extern char __ghsend_eventlog[]; + +extern TX_THREAD *_tx_thread_current_ptr; +UINT _tx_thread_interrupt_control(UINT new_posture); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the Event Log (in the format dictated by the */ +/* GHS Event Analyzer) and sets up various information for subsequent */ +/* operation. The start and end of the Event Log is determined by the */ +/* .eventlog section in the linker control file. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_initialize(VOID) +{ + +UCHAR *work_ptr; +UCHAR *read_ptr; +ULONG event_log_size; +UCHAR *end_ptr; +UINT i; + + + /* Clear total event counter. */ + _tx_el_total_events = 0; + + /* Clear event filter. */ + _tx_el_event_filter = 0; + + /* First, pickup the starting and ending address of the Event Log memory. */ + work_ptr = (unsigned char *) __ghsbegin_eventlog; + end_ptr = (unsigned char *) __ghsend_eventlog; + + /* Calculate the event log size. */ + event_log_size = end_ptr - work_ptr; + + /* Subtract off the number of bytes in the header and the TNI area. */ + event_log_size = event_log_size - (TX_EL_HEADER_SIZE + + (TX_EL_TNI_ENTRY_SIZE * TX_EL_TNIS)); + + /* Make sure the event log is evenly divisible by the event size. */ + event_log_size = (event_log_size/TX_EL_EVENT_SIZE) * TX_EL_EVENT_SIZE; + + /* Build the Event Log header. */ + + /* Setup the Event Log Version ID. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_VERSION_ID; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the TNIS (number of thread names) field. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_TNIS; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the EVPS (event pool size) field. */ + *((ULONG *) work_ptr) = event_log_size; + work_ptr = work_ptr + sizeof(ULONG); + + /* Remember the maximum number of events. */ + _tx_el_maximum_events = event_log_size/TX_EL_EVENT_SIZE; + + /* Setup max_events field. */ + *((ULONG *) work_ptr) = _tx_el_maximum_events; + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup the evploc (location of event pool). */ + *((ULONG *) work_ptr) = (ULONG) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Save the current event pointer. */ + _tx_el_current_event = (UCHAR **) work_ptr; + + /* Setup event_ptr (pointer to oldest event) field to the start + of the event pool. */ + *_tx_el_current_event = (UCHAR *) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup tbfreq (the number of ticks in a second) field. */ + *((ULONG *) work_ptr) = TX_EL_TICKS_PER_SECOND; + work_ptr = work_ptr + sizeof(ULONG); + + /* At this point we are pointing at the Thread Name Information (TNI) array. */ + + /* Remember the start of this for future updates. */ + _tx_el_tni_start = work_ptr; + + /* Clear the entire TNI array, this is the initial setting. */ + end_ptr = work_ptr + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE); + memset((void *)work_ptr, 0, (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = end_ptr; + + /* At this point, we are pointing at the actual Event Entry area. */ + + /* Remember the start of the actual event log area. */ + _tx_el_event_area_start = work_ptr; + + /* Clear the entire Event area. */ + end_ptr = work_ptr + event_log_size; + memset((void *)work_ptr, 0, event_log_size); + work_ptr = end_ptr; + + /* Save the end pointer for later use. */ + _tx_el_event_area_end = work_ptr; + + /* Setup an entry to resolve all activities from initialization and from + an idle system. */ + work_ptr = _tx_el_tni_start; + read_ptr = (UCHAR *) "Initialization/System Idle"; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID to NULL. */ + *((ULONG *) (_tx_el_tni_start + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) TX_NULL; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (_tx_el_tni_start + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Clear the time base global variables. */ + _tx_el_time_base_upper = 0; + _tx_el_time_base_lower = 0; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_register(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT i; + + + /* First of all, search for a free slot in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is available. */ + if (*(entry_ptr + TX_EL_TNI_VALID_OFFSET) == TX_EL_INVALID_ENTRY) + break; + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Check to see if there were no more valid entries. */ + if (i >= TX_EL_TNIS) + return(TX_EL_NO_MORE_TNI_ROOM); + + /* Otherwise, we have room in the TNI and a valid record. */ + + /* Setup the thread's name. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) thread_ptr; + + /* Setup the thread priority. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_PRIORITY_OFF)) = (ULONG) thread_ptr -> tx_thread_priority; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (entry_ptr + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Thread name has been registered. */ + return(TX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT found; +UINT i, j; + + + /* First of all, search for a match in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is a match. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + found = TX_TRUE; + j = 0; + do + { + + /* Determine if this character is the same. */ + if (*work_ptr != *read_ptr) + { + + /* Set found to false and fall out of the loop. */ + found = TX_FALSE; + break; + } + else if (*work_ptr == 0) + { + + /* Null terminated, just break the loop. */ + break; + } + else + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + } + + /* Increment the character count. */ + j++; + + } while(j < TX_EL_TNIS); + + + /* Was a match found? */ + if (found) + { + + /* Yes, mark the entry as available now. */ + *(entry_ptr + TX_EL_TNI_VALID_OFFSET) = TX_EL_INVALID_ENTRY; + + /* Get out of the loop! */ + break; + } + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Determine status to return. */ + if (found) + return(TX_SUCCESS); + else + return(TX_EL_NAME_NOT_FOUND); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_user_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user event into the event log. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* sub_type Event subtype for kernel call */ +/* info_1 First information field */ +/* info_2 Second information field */ +/* info_3 Third information field */ +/* info_4 Fourth information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT upper_tb; +UCHAR *entry_ptr; + + /* Disable interrupts. */ + TX_DISABLE + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_USER_EVENT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) sub_type; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) info_1; + + /* Store the second info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) = + (ULONG) info_2; + + /* Store the third info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) = + (ULONG) info_3; + + /* Store the fourth info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) = + (ULONG) info_4; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + /* Restore interrupts. */ + TX_RESTORE +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_running PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread change event into the event */ +/* log, which indicates that a context switch is taking place. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_schedule ThreadX scheduler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_running(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) 0; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) thread_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_preempted PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread preempted event into the event */ +/* log, which indicates that an interrupt occurred that made a higher */ +/* priority thread ready for execution. In this case, the previously */ +/* executing thread has an event entered to indicate it is no longer */ +/* running. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_context_restore ThreadX context restore */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_STATUS_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_READY; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt event into the log, which */ +/* indicates the start of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_INTERRUPT_SUB_TYPE; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt end event into the log, which */ +/* indicates the end of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt_end(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_END_OF_INTERRUPT; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_control PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function remaps the tx_interrupt_control service call so that */ +/* it can be tracked in the event log. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt posture */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_interrupt_control Interrupt control service */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_interrupt_control(UINT new_posture) +{ + +TX_INTERRUPT_SAVE_AREA +UINT old_posture; + + + TX_EL_NO_INTERRUPT_EVENTS + + TX_DISABLE + TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_INTERRUPT_CONTROL, _tx_thread_current_ptr, new_posture) + TX_RESTORE + + TX_EL_END_FILTER + + old_posture = _tx_thread_interrupt_control(new_posture); + return(old_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_on PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables all event filters. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_on(void) +{ + + /* Disable all event filters. */ + _tx_el_event_filter = TX_EL_ENABLE_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_off PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets all event filters, thereby turning event */ +/* logging off. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_off(void) +{ + + /* Set all event filters. */ + _tx_el_event_filter = TX_EL_FILTER_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the events filters specified by the user. */ +/* */ +/* INPUT */ +/* */ +/* filter Events to filter */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_filter_set(UINT filter) +{ + + /* Apply the user event filter. */ + _tx_el_event_filter = filter; +} + diff --git a/ports/cortex_r5/ghs/src/tx_ghs.c b/ports/cortex_r5/ghs/src/tx_ghs.c new file mode 100644 index 00000000..30b8054e --- /dev/null +++ b/ports/cortex_r5/ghs/src/tx_ghs.c @@ -0,0 +1,485 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" +#include +#include + +/* Allow these routines to access the following ThreadX global variables. */ +extern ULONG _tx_thread_created_count; +extern TX_THREAD *_tx_thread_created_ptr; +extern TX_THREAD *_tx_thread_current_ptr; + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +/* Thread-local storage routines for Green Hills releases 5.x and above. */ +/* + Thread-Local (Per-Thread) Library Data Retrieval + ================================================ + + __ghs_ThreadLocalStorage_specifier defines all library data items + that the Green Hills libraries allow to be allocated per-thread. + + An implementation can choose which of these data items to allocate + for each thread. For example, an implementation may choose to + allocate an errno value for each thread, but not the strtok_saved_pos + pointer. The application could then use strtok_r instead of strtok for + correct operation. + + To add per-thread library data, define one of the + TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item + or items in each thread control block TX_THREAD. + + If C++ with exceptions is being used, the __eh_globals entry must be + allocated for each thread. This is typically done by default using + TX_THREAD_EXTENSION_1 in tx_port.h. + + If __ghs_GetThreadLocalStorageItem is customized to return a + per-thread errno value, you should also: + + * Customize the System Library for your project + * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in + src/libsys/ind_errn.c + + If you customize the System Library, you should remove ind_thrd.c + from the libsys.gpj subproject. + + */ + +/* Provide global __eh_globals value to support C++ exception handling + outside a thread context. This name also forces this module to be + included in the linked program instead of the ind_thrd.o module from + the System Library libsys.a. + */ +static void *__eh_globals; + +#pragma ghs startnomisra +void *__ghs_GetThreadLocalStorageItem(int specifier) +{ + void *ptlsitem = (void *)0; + switch (specifier) { + case (int)__ghs_TLS_Errno: + /* Set ptslsitem to the address of the per-thread errno value. + The per-thread errno value should have the type int. + + If returning a per-thread errno value, follow the steps + above. + + This item is used by numerous library functions. + */ + break; + case (int)__ghs_TLS_SignalHandlers: + /* Set ptslsitem to the address of the per-thread SignalHandlers + array. The per-thread SignalHandlers array should have the + array type as in the following declaration: + SignalHandler SignalHandlers[_SIGMAX]; + The SignalHandler type and _SIGMAX constant are defined in + ind_thrd.h. + + This item is used by the library functions signal() and + raise(). + */ + break; + case (int)__ghs_TLS_asctime_buff: + /* Set ptslsitem to the address of the per-thread asctime_buff + array. The per-thread asctime_buff array should have the + array type as in the following declaration: + char asctime_buff[30]; + + This item is used by the library functions asctime() and + ctime(). The library provides asctime_r() and ctime_r(), + inherently thread-safe versions of these functions. + */ + break; + case (int)__ghs_TLS_tmpnam_space: + /* Set ptslsitem to the address of the per-thread tmpnam_space + array. The per-thread tmpnam_space array should have the + array type as in the following declaration: + char tmpnam_space[L_tmpnam]; + The constant is defined in + + This item is used by the library function tmpnam() when + passed NULL. The library provides tmpnam_r(), an + inherently thread-safe version of tmpnam(). + */ + break; + case (int)__ghs_TLS_strtok_saved_pos: + /* Set ptslsitem to the address of the per-thread + strtok_saved_pos pointer. The per-thread strtok_saved_pos + pointer should have the type "char *". + + This item is used by the library function strtok(). + The library provides strtok_r(), an inherently thread-safe + version of strtok(). + */ + break; + case (int)__ghs_TLS_gmtime_temp: + /* Set ptslsitem to the address of the per-thread gmtime_temp + value. The per-thread gmtime_temp value should have the + type "struct tm" defined in time.h, included by indos.h. + + This item is used by the library functions gmtime() and + localtime(). The library provides gmtime_r() and + localtime_r(), inherently thread-safe versions of these + functions. + */ + break; + case (int)__ghs_TLS___eh_globals: + /* Set ptslsitem to the address of the per-thread __eh_globals + value. The per-thread __eh_globals value should have the + type "void *". + + This item is used by C++ exception handling. + */ + if (_tx_thread_current_ptr) + ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + ptlsitem = (void *)&__eh_globals; + break; + } + return ptlsitem; +} +#pragma ghs endnomisra +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ + +/* + * ThreadX C and C++ thread-safe library support routines. + * + * This implementation merely tries to guarantee thread safety within + * individual C library calls such as malloc() and free(), but it does + * not attempt to solve the problems associated with the following + * multithreaded issues: + * + * 1. Use of errno. This can be made thread-safe by adding errno + * to TX_THREAD_PORT_EXTENSION and using that within a modified + * version of libsys/ind_errno.c. + * + * 2. Thread safety ACROSS library calls. Certain C library calls either + * return pointers to statically-allocated data structures or maintain + * state across calls. These include strtok(), asctime(), gmtime(), + * tmpnam(NULL), signal(). To make such C library routines thread-safe + * would require adding a ThreadLocalStorage struct to the thread control + * block TX_THREAD. Since relatively few applications make use of these + * library routines, the implementation provided here uses a single, global + * ThreadLocalStorage data structure rather than greatly increasing the size + * of the thread control block TX_THREAD. + * + * The ThreadX global variable _tx_thread_current_ptr points to the + * current thread's control block TX_THREAD. If a ThreadLocalStorage struct + * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage + * should be modified to return &(_tx_thread_current_ptr->tx_tls). + */ + +static ThreadLocalStorage GlobalTLS; + +ThreadLocalStorage *GetThreadLocalStorage() +{ + return &GlobalTLS; +} +#endif + +/* + * Use a global ThreadX mutex to implement thread safety within C and C++ + * library routines. + * + */ +TX_MUTEX __ghLockMutex; + +/* + * Acquire general lock. Blocks until the lock becomes available. + * Use tx_mutex_get to implement __ghsLock + */ +void __ghsLock(void) +{ + tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER); +} + +/* + * Release general lock + * Use tx_mutex_put to implement __ghsUnlock + */ +void __ghsUnlock(void) +{ + tx_mutex_put(&__ghLockMutex); +} + +/* ThreadX Initialization function prototype. */ +void _tx_initialize_kernel_setup(void); + +void __gh_lock_init(void) +{ + /* Initialize the low-level portions of ThreadX. */ + _tx_initialize_kernel_setup(); + + /* Create the global thread lock mutex. */ + tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT); +} + +/* + Saving State Across setjmp() Calls + ================================== + + These routines can be used to save and restore arbitrary state + across calls to setjmp() and longjmp(). +*/ +int __ghs_SaveSignalContext(jmp_buf jmpbuf) +{ + return 0; +} + +/* Restore arbitrary state across a longjmp() */ +void __ghs_RestoreSignalContext(jmp_buf jmpbuf) +{ +} + +#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560) +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +#else + if (_tx_thread_current_ptr) + + /* Return thread-specific __eh_globals pointer. */ + return _tx_thread_current_ptr->tx_thread_eh_globals; + else + /* Return the global __eh_globals pointer. */ + return GlobalTLS.__eh_globals; +#endif +} +#endif + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +#pragma weak __cpp_exception_init +extern void __cpp_exception_init(void **); +#pragma weak __cpp_exception_cleanup +extern void __cpp_exception_cleanup(void **); + +/* __tx_cpp_exception_init retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_init. + */ +void __tx_cpp_exception_init(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_init) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_init(peh_globals); + } +} + +/* __tx_cpp_exception_cleanup retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_cleanup. + */ +void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_cleanup) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_cleanup(peh_globals); + } +} + +/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize + exceptions for the global context. + */ +void __ghs_cpp_exception_init() { + __tx_cpp_exception_init((void *)0); +} + +/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up + exceptions for the global context. + */ +void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + __tx_cpp_exception_cleanup((void *)0); +} +#endif + + +/* + File Locks + ====================== + + These routines can be customized to implement per-file locks to allow + thread-safe I/O. + +*/ + +/* Acquire lock for FILE *addr */ +void __ghs_flock_file(void *addr) +{ + tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER); +} + +/* Release lock for FILE *addr */ +void __ghs_funlock_file(void *addr) +{ + tx_mutex_put((TX_MUTEX *)addr); +} + +/* Non blocking acquire lock for FILE *addr. May return -1 if */ +/* not implemented. Returns 0 on success and nonzero otherwise. */ +int __ghs_ftrylock_file(void *addr) +{ + return -1; +} + +/* Calls to initialize local lock data structures before they */ +/* are used. */ +void __ghs_flock_create(void **addr) +{ + *addr = (void *)(&__ghLockMutex); +} +void __ghs_flock_destroy(void *addr) {} + + +/* + * ThreadX Peak Stack Checking support routines. + * + * All of these routines are called by MULTI's ThreadX-aware debugging + * package to determine the peak stack use for one thread or for all threads. + * + * These routines are included in this file in order to guarantee that they will + * be available while debugging with MULTI. These routines are not referenced by + * any other part of the ThreadX system. + * + * _txs_thread_stack_check: return the peak stack usage for a thread. + * + * _txs_thread_stack_check_2: store the peak stack usage for all threads + * in the tx_thread_stack_size field of each thread + * control block, TX_THREAD. This routine takes + * advantage of the redundancy within the TX_THREAD + * structure since tx_thread_stack_size can be computed + * from the tx_thread_stack_start and tx_thread_stack_end + * fields of TX_THREAD. + * + * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2 + * call by computing the stack size for each + * thread and storing the result in the + * tx_thread_stack_size field of each thread control + * block TX_THREAD. + * + * These three routines do not support architectures such as i960 or StarCore + * where the stack grows up instead of down. + * + */ +#ifndef TX_DISABLE_STACK_CHECKING + +ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr) +{ + CHAR *cp; /* Pointer inside thread's stack. */ + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start; + cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Return the number of bytes from cp up to and including the + end of the stack. */ + return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1); + } + } + return thread_ptr->tx_thread_stack_size; +} + + +int _txs_thread_stack_check_2(void) { + CHAR * cp; /* Pointer inside thread's stack. */ + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end; + ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Store the number of bytes from cp up to and including the + end of the stack in the tx_thread_stack_size field. */ + tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1; + break; + } + + } + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +int _txs_thread_stack_check_2_fixup(void) { + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and + tx_thread_stack_start fields. */ + tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1; + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +#endif /* TX_DISABLE_STACK_CHECKING */ diff --git a/ports/cortex_r5/ghs/src/tx_ghse.c b/ports/cortex_r5/ghs/src/tx_ghse.c new file mode 100644 index 00000000..6369df77 --- /dev/null +++ b/ports/cortex_r5/ghs/src/tx_ghse.c @@ -0,0 +1,49 @@ +/* + * ThreadX C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 560) +#ifdef _WIN32 +/* Windows uses a different linker, so include a stub routine, never called, + to pull in __cpp_exception_init and __cpp_exception_cleanup */ +extern void __cpp_exception_init(void **); +extern void __cpp_exception_cleanup(void **); +void __tx_win32_pull_in_exceptions(void) { + __cpp_exception_init(0); + __cpp_exception_cleanup(0); +} +#else +#pragma ghs reference __cpp_exception_init +#pragma ghs reference __cpp_exception_cleanup +#endif + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +} +#endif diff --git a/ports/cortex_r5/green/src/tx_thread_context_restore.arm b/ports/cortex_r5/ghs/src/tx_thread_context_restore.arm similarity index 96% rename from ports/cortex_r5/green/src/tx_thread_context_restore.arm rename to ports/cortex_r5/ghs/src/tx_thread_context_restore.arm index d15c2a41..7662dcf1 100644 --- a/ports/cortex_r5/green/src/tx_thread_context_restore.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -114,13 +114,13 @@ _tx_thread_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -132,7 +132,7 @@ _tx_thread_context_restore: __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -221,7 +221,7 @@ _tx_skip_irq_vfp_save: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - + STR r2, [r0, 24] # Save thread's time-slice MOV r2, 0 # Clear value STR r2, [r3] # Disable global time-slice flag diff --git a/ports/cortex_r5/green/src/tx_thread_context_save.arm b/ports/cortex_r5/ghs/src/tx_thread_context_save.arm similarity index 94% rename from ports/cortex_r5/green/src/tx_thread_context_save.arm rename to ports/cortex_r5/ghs/src/tx_thread_context_save.arm index ef250e66..e27b5135 100644 --- a/ports/cortex_r5/green/src/tx_thread_context_save.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,40 +39,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -90,7 +90,7 @@ _tx_thread_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers #ifdef TX_ENABLE_FIQ_SUPPORT #ifdef TX_BEFORE_ARMV6 @@ -116,7 +116,7 @@ _tx_thread_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -132,7 +132,7 @@ _tx_thread_context_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing __tx_thread_not_nested_save: /* } */ @@ -146,13 +146,13 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, r10, r12, lr} # Store other registers /* Save the current stack pointer in the thread's control block. */ @@ -172,7 +172,7 @@ __tx_thread_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing /* } else @@ -182,7 +182,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit @@ -197,7 +197,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, 16 # Recover saved registers - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing .type _tx_thread_context_save,$function .size _tx_thread_context_save,.-_tx_thread_context_save diff --git a/ports/cortex_r5/green/src/tx_thread_fiq_context_restore.arm b/ports/cortex_r5/ghs/src/tx_thread_fiq_context_restore.arm similarity index 96% rename from ports/cortex_r5/green/src/tx_thread_fiq_context_restore.arm rename to ports/cortex_r5/ghs/src/tx_thread_fiq_context_restore.arm index 3cf756c8..e1f1669f 100644 --- a/ports/cortex_r5/green/src/tx_thread_fiq_context_restore.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_fiq_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -42,41 +42,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_restore Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_restore Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the fiq interrupt context when processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* FIQ ISR Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the fiq interrupt context when processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* FIQ ISR Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -111,13 +111,13 @@ _tx_thread_fiq_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_fiq_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -129,7 +129,7 @@ _tx_thread_fiq_context_restore: __tx_thread_fiq_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -188,7 +188,7 @@ __tx_thread_fiq_preempt_restore: MOV r5, SVC_MODE # Build SVC mode CPSR MSR CPSR_c, r5 # Enter SVC mode STMDB sp!, {r0-r3} # Save r0-r3 on thread's stack - + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer diff --git a/ports/cortex_r5/green/src/tx_thread_fiq_context_save.arm b/ports/cortex_r5/ghs/src/tx_thread_fiq_context_save.arm similarity index 93% rename from ports/cortex_r5/green/src/tx_thread_fiq_context_save.arm rename to ports/cortex_r5/ghs/src/tx_thread_fiq_context_save.arm index a8b277d4..6e34bb4a 100644 --- a/ports/cortex_r5/green/src/tx_thread_fiq_context_save.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_fiq_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -33,40 +33,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_save Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_save Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -84,7 +84,7 @@ _tx_thread_fiq_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state CMP r2, 0 # Is this the first interrupt? @@ -99,7 +99,7 @@ _tx_thread_fiq_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -115,7 +115,7 @@ _tx_thread_fiq_context_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing __tx_thread_fiq_not_nested_save: /* } */ @@ -129,16 +129,16 @@ __tx_thread_fiq_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, lr} # Store other registers, Note that we don't - /* # need to save sl and ip since FIQ has - # copies of these registers. Nested + /* # need to save sl and ip since FIQ has + # copies of these registers. Nested # interrupt processing does need to save # these registers. */ @@ -159,7 +159,7 @@ __tx_thread_fiq_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing /* } else @@ -179,15 +179,15 @@ __tx_thread_fiq_idle_system_save: #endif /* Not much to do here, save the current SPSR and LR for possible - use in IRQ interrupted in idle system conditions, and return to + use in IRQ interrupted in idle system conditions, and return to FIQ interrupt processing. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, lr} # Store other registers that will get used - /* # or stripped off the stack in context + /* # or stripped off the stack in context # restore */ - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing .type _tx_thread_fiq_context_save,$function .size _tx_thread_fiq_context_save,.-_tx_thread_fiq_context_save diff --git a/ports/cortex_r5/green/src/tx_thread_fiq_nesting_end.arm b/ports/cortex_r5/ghs/src/tx_thread_fiq_nesting_end.arm similarity index 91% rename from ports/cortex_r5/green/src/tx_thread_fiq_nesting_end.arm rename to ports/cortex_r5/ghs/src/tx_thread_fiq_nesting_end.arm index e87f701b..dd34711d 100644 --- a/ports/cortex_r5/green/src/tx_thread_fiq_nesting_end.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_fiq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,48 +41,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_end Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_end Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ -/* processing from system mode back to FIQ mode prior to the ISR */ -/* calling _tx_thread_fiq_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ +/* processing from system mode back to FIQ mode prior to the ISR */ +/* calling _tx_thread_fiq_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_fiq_nesting_start.arm b/ports/cortex_r5/ghs/src/tx_thread_fiq_nesting_start.arm similarity index 92% rename from ports/cortex_r5/green/src/tx_thread_fiq_nesting_start.arm rename to ports/cortex_r5/ghs/src/tx_thread_fiq_nesting_start.arm index c51476d8..e3c6aee7 100644 --- a/ports/cortex_r5/green/src/tx_thread_fiq_nesting_start.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_fiq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,45 +39,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_start Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_start Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_context_save has been called and switches the FIQ */ -/* processing to the system mode so nested FIQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_context_save has been called and switches the FIQ */ +/* processing to the system mode so nested FIQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_interrupt_control.arm b/ports/cortex_r5/ghs/src/tx_thread_interrupt_control.arm similarity index 92% rename from ports/cortex_r5/green/src/tx_thread_interrupt_control.arm rename to ports/cortex_r5/ghs/src/tx_thread_interrupt_control.arm index 27465d58..2d2759ed 100644 --- a/ports/cortex_r5/green/src/tx_thread_interrupt_control.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_interrupt_control.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,39 +38,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_interrupt_disable.arm b/ports/cortex_r5/ghs/src/tx_thread_interrupt_disable.arm similarity index 92% rename from ports/cortex_r5/green/src/tx_thread_interrupt_disable.arm rename to ports/cortex_r5/ghs/src/tx_thread_interrupt_disable.arm index c62743b4..5d8be886 100644 --- a/ports/cortex_r5/green/src/tx_thread_interrupt_disable.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_interrupt_disable.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,38 +38,38 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_interrupt_restore.arm b/ports/cortex_r5/ghs/src/tx_thread_interrupt_restore.arm similarity index 92% rename from ports/cortex_r5/green/src/tx_thread_interrupt_restore.arm rename to ports/cortex_r5/ghs/src/tx_thread_interrupt_restore.arm index 76d38dc8..fa797c71 100644 --- a/ports/cortex_r5/green/src/tx_thread_interrupt_restore.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_interrupt_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -31,39 +31,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_irq_nesting_end.arm b/ports/cortex_r5/ghs/src/tx_thread_irq_nesting_end.arm similarity index 91% rename from ports/cortex_r5/green/src/tx_thread_irq_nesting_end.arm rename to ports/cortex_r5/ghs/src/tx_thread_irq_nesting_end.arm index 49c3bee5..e2e6ac6d 100644 --- a/ports/cortex_r5/green/src/tx_thread_irq_nesting_end.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_irq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -40,48 +40,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_end Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_end Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ -/* processing from system mode back to IRQ mode prior to the ISR */ -/* calling _tx_thread_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ +/* processing from system mode back to IRQ mode prior to the ISR */ +/* calling _tx_thread_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_irq_nesting_start.arm b/ports/cortex_r5/ghs/src/tx_thread_irq_nesting_start.arm similarity index 92% rename from ports/cortex_r5/green/src/tx_thread_irq_nesting_start.arm rename to ports/cortex_r5/ghs/src/tx_thread_irq_nesting_start.arm index 487a3c60..464b297f 100644 --- a/ports/cortex_r5/green/src/tx_thread_irq_nesting_start.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_irq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,45 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_start Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_start Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_context_save has been called and switches the IRQ */ -/* processing to the system mode so nested IRQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_context_save has been called and switches the IRQ */ +/* processing to the system mode so nested IRQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r5/green/src/tx_thread_schedule.arm b/ports/cortex_r5/ghs/src/tx_thread_schedule.arm similarity index 96% rename from ports/cortex_r5/green/src/tx_thread_schedule.arm rename to ports/cortex_r5/ghs/src/tx_thread_schedule.arm index 3d7fe7ea..6fab3773 100644 --- a/ports/cortex_r5/green/src/tx_thread_schedule.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_schedule.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,42 +38,42 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -111,7 +111,7 @@ __tx_thread_schedule_loop: /* } while(_tx_thread_execute_ptr == TX_NULL); */ - + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ @@ -134,7 +134,7 @@ __tx_thread_schedule_loop: MOV r0, v1 # Restore temp register #endif - LDR r1, =_tx_thread_current_ptr # Pickup address of current thread + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread STR r0, [r1] # Setup current thread pointer /* Increment the run count for this thread. */ @@ -148,7 +148,7 @@ __tx_thread_schedule_loop: /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - LDR r2, =_tx_timer_time_slice # Pickup address of time slice + LDR r2, =_tx_timer_time_slice # Pickup address of time slice /* # variable */ LDR sp, [r0, 8] # Switch stack pointers STR r3, [r2] # Setup time-slice @@ -199,7 +199,7 @@ _tx_skip_solicited_vfp_restore: .type _tx_thread_schedule,$function .size _tx_thread_schedule,.-_tx_thread_schedule - + #ifdef __VFP__ .globl tx_thread_vfp_enable tx_thread_vfp_enable: diff --git a/ports/cortex_r5/green/src/tx_thread_stack_build.arm b/ports/cortex_r5/ghs/src/tx_thread_stack_build.arm similarity index 96% rename from ports/cortex_r5/green/src/tx_thread_stack_build.arm rename to ports/cortex_r5/ghs/src/tx_thread_stack_build.arm index 1d4a22ea..910b72ca 100644 --- a/ports/cortex_r5/green/src/tx_thread_stack_build.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_stack_build.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ /* thread_ptr Pointer to thread control blk */ /* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -86,10 +86,10 @@ .globl _tx_thread_stack_build _tx_thread_stack_build: - + /* Build a fake interrupt frame. The form of the fake interrupt stack on the Cortex-R5 should look like the following after it is built: - + Stack Top: 1 Interrupt stack frame type CPSR Initial value for CPSR r0 (a1) Initial value for r0 diff --git a/ports/cortex_r5/green/src/tx_thread_system_return.arm b/ports/cortex_r5/ghs/src/tx_thread_system_return.arm similarity index 94% rename from ports/cortex_r5/green/src/tx_thread_system_return.arm rename to ports/cortex_r5/ghs/src/tx_thread_system_return.arm index 87e1377a..6e89e681 100644 --- a/ports/cortex_r5/green/src/tx_thread_system_return.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_system_return.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -37,41 +37,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -90,7 +90,7 @@ _tx_thread_system_return: LDR r4, =_tx_thread_current_ptr # Pickup address of current ptr LDR r5, [r4] # Pickup current thread pointer - + #ifdef __VFP__ LDR r1, [r5, 144] # Pickup the VFP enabled flag CMP r1, 0 # Is the VFP enabled? @@ -104,7 +104,7 @@ _tx_skip_solicited_vfp_save: MOV r0, #0 # Build a solicited stack type MRS r1, CPSR # Pickup the CPSR STMDB sp!, {r0-r1} # Save type and CPSR - + /* Lockout interrupts. */ #ifdef TX_BEFORE_ARMV6 diff --git a/ports/cortex_r5/green/src/tx_thread_vectored_context_save.arm b/ports/cortex_r5/ghs/src/tx_thread_vectored_context_save.arm similarity index 95% rename from ports/cortex_r5/green/src/tx_thread_vectored_context_save.arm rename to ports/cortex_r5/ghs/src/tx_thread_vectored_context_save.arm index 01a57231..3255d0f5 100644 --- a/ports/cortex_r5/green/src/tx_thread_vectored_context_save.arm +++ b/ports/cortex_r5/ghs/src/tx_thread_vectored_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,40 +39,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_vectored_context_save Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_vectored_context_save Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -139,7 +139,7 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Note: Minimal context of interrupted thread is already saved. */ @@ -171,7 +171,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit diff --git a/ports/cortex_r5/green/src/tx_timer_interrupt.arm b/ports/cortex_r5/ghs/src/tx_timer_interrupt.arm similarity index 95% rename from ports/cortex_r5/green/src/tx_timer_interrupt.arm rename to ports/cortex_r5/ghs/src/tx_timer_interrupt.arm index aaf7b9b2..219e2a19 100644 --- a/ports/cortex_r5/green/src/tx_timer_interrupt.arm +++ b/ports/cortex_r5/ghs/src/tx_timer_interrupt.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ @@ -32,43 +32,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-R5/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-R5/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Process timer expiration */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Process timer expiration */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -95,7 +95,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR r3, =_tx_timer_time_slice # Pickup address of time-slice + LDR r3, =_tx_timer_time_slice # Pickup address of time-slice LDR r2, [r3] # Pickup time-slice CMP r2, 0 # Is it non-active? BEQ __tx_timer_no_time_slice # Yes, skip time-slice processing @@ -212,7 +212,7 @@ __tx_timer_dont_activate: /* if (_tx_timer_expired_time_slice) { */ - LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired + LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired LDR r2, [r3] # Pickup the actual flag CMP r2, 0 # See if the flag is set BEQ __tx_timer_not_ts_expiration # No, skip time-slice processing diff --git a/ports/cortex_r5/ghs/src/txr_ghs.c b/ports/cortex_r5/ghs/src/txr_ghs.c new file mode 100644 index 00000000..19572e2b --- /dev/null +++ b/ports/cortex_r5/ghs/src/txr_ghs.c @@ -0,0 +1,84 @@ +/* + * ThreadX API Runtime Error Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +/* #include "tx_ghs.h" */ +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* Customized ThreadX API runtime error support routine. */ + +void _rnerr(int num, int linenum, const char*str, void*ptr, ...); + +/* __ghs_rnerr() + This is the custom runtime error checking routine. + This implementation uses the existing __rnerr() routine. + Another implementation could use the .syscall mechanism, + provided MULTI was modified to understand that. + */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal) { + TX_INTERRUPT_SAVE_AREA + int num; + /* + Initialize the stack levels value. + + Add 3 to account for the calls to _rnerr, __rnerr, and + __ghs_rnerr. + + If the implementation changes, calls to __ghs_rnerr + will not need to be changed. + + Zero is not permitted, so substitute 3 in that case. + */ + num = (stackLevels+3) & 0xf; + if (!num) { + num = 3; + } + /* + Shift the stack levels value to bits 12..15 and + insert the stack trace display value in bit 11. + Bits 0..10 are unused. + */ + num = (num << 12) | (stackTraceDisplay ? 0x800 : 0); + + /* This will mask all interrupts in the RTEC code, which is probably + unacceptable for many targets. */ + TX_DISABLE + _rnerr(num, -1, (const char *)hexVal, (void *)errMsg); + TX_RESTORE +} + + +/* ThreadX thread stack checking runtime support routine. */ + +extern char __ghsbegin_stack[]; +extern TX_THREAD *_tx_thread_current_ptr; + +void __stkchk(void) { + int i; + if(_tx_thread_current_ptr) + { + if((unsigned)(&i) <= + (unsigned)(_tx_thread_current_ptr -> tx_thread_stack_start)) + { + _rnerr(21, -1, 0, 0); + } + } + else + { + if((unsigned)(&i) <= (unsigned)__ghsbegin_stack) + { + _rnerr(21, -1, 0, 0); + } + } +} diff --git a/ports/cortex_r5/gnu/example_build/libc.a b/ports/cortex_r5/gnu/example_build/libc.a deleted file mode 100644 index 5b04fa4ed9b6479f70979f5577dd0705d1f6d1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2447586 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

Q>dP6leU^u3WJIStz4({jFOv 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 z%0D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܌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 z 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

P(t+jpEJ6LGE?^@5sYG>cI7DtwSS83U3 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�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�EK@7DEt$^GEFD^6Oiv#`KyY;KY?)Jp8#^gF!c)m1aK%WR?5OZ z0i3C_@J~Q4R$2HbAPfHlQ1~Z+!ao5N{t2M)PXL8~0?2zd^$Y(5Q1~Z+uV}dNPe9(S zvhYtp7XAsK@J|4Re*!4{6Ts=P5>T)3PXLQm7XAsy!ao5N{t2M)PXL8~0x0|wK;fSN zzM=Vre**G*DhvMvWZ|Cx3jYL9_$PqEKLI=k4<*(|_$Poe5gaJ|6F}jg01E#EQ1~Z+ z!ao6gNAnB+1Z3f#01E#EQ1~Z+!ao5N{t2M)PXL8~0@$cFIpLpxe1pouKLPnsm4$x- z@{d&({t3ttJXF~Z;hz8s{{&F@CxF5~0c_WF!aD((4;$L(B8o>45k6WmkK-Hhe10E< z4bNX1*qsgUgcEwcF7RnQ3*mel-VvzPo-@b)#%fuxgZom0f4F=Fgp(A+QUe58PePW-9{W%|)6>@(ZZTvpahse{|Zk z9j>*wEt0%3%}qY*x<%`)Uej9a)0l3{tR17k!_fq*)&O8{=%P;4=hjGa)2>MJ(p{0n zt?fr5E|S>L9=VK0G8+k1!VWa^dyy(f_F1bvigZEBBZ zHv=zik0fpf-U9iS_DJjW9VR?Ft)*?0wQQ$b)Y2BMU$^6HQom*BrD6!9&ddb-*rOoZTDJpFT#3(?_}g|3L|h*9Adn5YP-GOt0zFgOymRQ#s!TGY2YoYhD zcGK1sOL zhw|69e_P54pVt{X%=vV9KDC`J$Bgmp#N|62^ofhJToxM@V#HL-@62A#!Vk5?c*Z#rDjqBO|oW#cUgON6o zy%A%gV_%u^ZDGs?{jpK|(M z?TBsx4tphH%mfA87O?JT68=YM-2*3330~NAK{Mx@Vah@V08@fq0mUt7;WB0%8YFaNitW#>B;it zBTyK3+4u)bnCO*b#f0sDk|}i>HT!cF7NI4MKUYC`$Njkqan|p6FWn~rH zO{dTCDjSKtMn(SeW!A+xsO8Idp%Ct}aUyg2%a@0;@|~A2Ph=D7W+AE%*Bw8Loc=Bw zTu}#$CFd$Q;pNNDeO^VQu}fKizkK;e)YW16vg0pbewKy%%a=KG#POFeH=1O8(#3*H$TQ% zERMf?c^OOhmoL9hDOkRIHLKnI^5s*Y|KDr*as)N9BF;_#)Y-aoowmz7va6Hd;uzdMT%o{9U#=D5+1k0D_Qw)|b|B+&_eEB!*=3x2qO)NK< ztFWB~1<;z*De>cmQc?vo#U&c@!!fO?$NtQ2M%a(UXm;uvRjw}T?;cryR#6*d|;Oj)% zW2n9Tj)bP2&D?|2nLg(fyjK4*Assb#6JwN?4 zghTuF!09h3-Z9$GNNX3*27$aC2TSaLbS?fBw9Q$4;E!_P@;SgTFggj7uU}%kr_uImZrIKF?h^cf+w~96N8q zIJXZJS&Fg5B}|+pbbzJyRA?NH zRqJqMW8BWhVcb*=Pjm`@7~zlM*pA~B98crm1ID;F;OFIY3(9x^$8E^pg5yRUx8T@_ z<0>4+JrDnO98Al4@dG`tpVxqI;V`bln%<7v-g`U7SJsv^EN`^@Mg1TEE}UYeDrzbk zmRHo4U1(uEUyzct^jQbbgBmPu@3epY>as@J&%QB*Sz%2T7_PO;6Z=x6rm9OTy}ELD zjheFZ#wM$-y1bq*gLh}Z9|%6by~Q;x@<)$DpXy^W|v*i zgkrsZ>zJ+q!=nZh_+;N5kEo!ws;+duH@9wXk2*alxSdJocQr?d4KAtns1?FI3yt;F zRnq0`Q?v~2*q3{u24f8?S}{^ z^(lW_s8qlUG5^dN^HK|E%{Xh}qB&>JN-g#nJA%&jcqF8U0uBwY2IVCeqLfAsJ6>B} zO;V@MnLn$;Y@$@72?|C+3NsursA|h`s#Y~LRMa++X`-Z>jVl9fZ!KuJ{OLzB#;w&D z7Aba+&euy_P{Ki*^4J2{*ws=Y-S#NuE$8&AUs}CVYv-M)RLKP??Vu`jkPr#u8CX++ zkw$*sx|ONw5?rUjthbVKRMl*_v%I1SBd3Bz1obaN$nv^|3vr3F|7Cows$JDs0s0f} z=Y?}6`aOyajGMYuC_pYe4u007`nbXpZ-8L1q$*dTkNhjdyNBU&1R)2zp;kCLyaK$Y zc+?zTKl&^lXqB`a1Lz{$99E-SWn4&C34NzrS^o!TK%w!=G4CzEyrOi~a-MZ7zpSpN z21C)x2S;>%NmElpRp~0ujq9=NYCcr>v5P01IGXzK(Dog_$t{7zb>MueG97*_?89sD zo>71}I`ae`3qRAFC+Ggs@rBsrf!AU>EX(H$qtEafnEY2m-)18KjlO%-&+|+-84X>5^I;s*@pwqlgx`+OqT&8C`kp`n zjH!B)?k7k$+z3FEuAi6Ak9imAp7*679n<42(RYh+!89WfGzb=Tu)RD#>=QGV;k8~% zc$~lV)0uKTDfk}_|5weM+QLSN3)hD2(ciKfdpP(HRF!}W9%1$&9q&1uM@7ahC-UyW`B^4tRjs7J z!?C9B0=@u*p3x{`FpeDDFWw^#@a8h*VEo^R?#;pYZpMHjZYLt1p9L5{n23RO5Bh@m zgd)$6bA)$)>Xz357oq&t0v?0%9U%LR_%X#(6;D@WA2EJ~BJVAfuT|u=O!;2LM-{gz zzNGl3V!PrVMfL;pbNv@FS5el&K$cIkK;d-&ep=(X#)|2M*8zC1%8x2;Rs5miFBJDE z%9l;#6J7_P@HzlTqu;4lcpZSk>i{g)aN%`;e6h;H>i}7J9e~2?02E#apzt~XkHGn( zUg32B3ai`s92cYme0Og}GPd`<;dKBC zuLE$GrW0NV$inLY6kZ2l0{2(yq0N*iybi$ARTf?c$SYMAUI)m+>i`s92jEvUUU(fK ze^+JUb$~3q4nW~`01B@Ia6r0OuJAfQ7G4LS@Hzm6*8wQJ4nW~`01B@IP&0Vuo&0Vuoi`s92cYme0EO29D09Go1MqNRJ!dH{ zP%KrfC1R{LC|;=H8x(I+d|2@r#g`O+uJ{|pzbdBT_Rn$$DCQ~_D1KaVxneC5^;k{B zoVANJ{0fz?Rrz+suWR_@D!-ue%PPM`guYhAb`5`5F$3>+EGLtQdLBtc`VoqwG<>4s zxrzrrhYqjUJ_|2MuE#Q2!-Zsj>t>*0r^rxX5Yub&%-ygvoy}#|J6uV zam&v1Q?C0pp1M16iDN#Vr-Qm!KZaA!eCV-@FW-@6oxSsqd-uL+;@?31>7TmgruM$A zEp0j2Gpe<}S7!U(y}cR7vY<=SQRiDaXJ-%EjSqS|>yvk;aSonUe?!{rlABS-v!(y= zuG>2@?ep#Sw8dzzH8-;0^44gm9EL}VX6-=z&a;X?FZocnp;MF8v4~&zhqAx-Xj?gF zh;8OrxB$)YwUzc76- zne2k--ahFEgjkT4vu`rF1s0|TFo}x_U|tTyES;hQg-q1f?30zP$Zq=&k=5$V{9EJd%*?7)x*-^zsdky+qAEnTgDe@X1VC zn9?UR5#|Y>%;e|H*MZE$=|E=UbRaWvzT=fQHTGRL-6u2oBTMK&X5#o{CTB3^3tqnY zvClB&i=I>z`zhP!lbL*p(yLpU0%Rsm6+LPFSzntOLS!Zs(cdH+aCV>toFFs|LjUrP zi75`y#GizJAaj729RK@Yi+?t|&C@GBP0Fnh`rpfVzg*1QL_{Y=SYc2j^e*+Ey zU&asJF?hsnMwmV+xQVGdYg2MvTT?j&&nu#?N8mlQeI3{8!Ww zkePgfiBHkkWc(H;4#-UKW`;{lW2eOBS581?BE1%nneax52|`{aXU0EGRRNjFX*?$Z znaSHse5NKYihq|)2*^xsXX1d&gjXge2sI;-Ob~jWa)`|2Ix4%+AsvK}ncPNgAu^NE zjNg~cq#xCU$V~WwN+t-&4Im&h>CJS#

81Wqb<71CW_;{Ky2MBiI80naO763&>1l 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 E0gsN{S^xk5 diff --git a/ports/cortex_r5/gnu/example_build/libgcc.a b/ports/cortex_r5/gnu/example_build/libgcc.a deleted file mode 100644 index d735349678a569441a4c7c45d9ce62d9b9b783e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260152 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 z z=erFtC^$_pB8aJrj-M;INU%zf&wr+0A=oU~A-G1cSMYMdt%BDJ_6go8NL>OqWXi{S zOci<>5j58`8PD>OE)$yNB7L6FD}?SAn&n~qRYJ2Iq_+wEu+Te%en#l`h2AH03ZLu9 zKb?s5vze#$PUUpU`&*&3zphzg_6(g?>rsexdgWt@j@X zzn*76eh@#V=RB18%@O)Eq4C%lcHiud&fKn^jtAgZ6 z>@Bf3HTcxtK=A+UabYv*!5$O)SL{ten1cOtFuiY=0;o18w*glGcL1?( z%*fw^_f_~)P}bz28gO8D?Vh{=Hs;6=Hlsg@EQs3;!{$}n_h-^h_$*^5eC7_Mp$+d- z`1NiXI0|)P+CbG+1FF}YKRH*8*^RR<$KsddMPivrk&PH6`*t&)_TS`ZI?ADqGV6Ai zuWrxNZdaX6duxpNeY;_A`@yKW3i|`p?mkM@j>et=qn)ZgZzswJ`&VuIo&;HxtNKhC z^nL)6594gO-p?V@kG(d$fzLvHyQ`|4OtK3+7+WtKLDNx zd(YQvV--L|kkY*mz{1{jc_^^78%()T546*6+6J>u_WJM| z>volE%l0?}GJ4TwOz&3Fz8UuTupwR_3=AAIYhyooc%2KtmyA72$VdK9P=@p!;EMzX zMn8L1Kl8cdm8-P;cz!#!>}5Y$BW*tgGO(JZ^EwxG+K)beHv31gZ`ZZLGXXrPx8`BJ z_Cr4Fg*xl&cMRg$w^2>d|q7;spEYF^PuI*uut|Z_`xy(AL$M2>6VO6GYncO2v|S zf;EGlXpNS$d?^#`lxbB>t88mkJ!qfI!kiGVK|-RT*46QCSrfR_YXLl8`Ir-H=JX^@ zs73_hkOJ3+&mIS;8|&3rQx8*66H44gr0<^^~G zSIOFHIGGF4Ny*3He?;=r3{Jie=~9v(M+mH$Q^0DhncV}3b!s1D9@u(Gk)$c|)6C|4 zeU=G~7FQz1`6^d zsKEWmXH=jF;c4H+A5DL+C)4+A)?Bc0zRx&j&1JMf)8Cnl^O^qYG}BqS`&J^sr~t41 zF@e#af%pwm8e>iP^!GF8{4sZv`BN@RIaOnBAd47ljy1-bS|ThUM@NvAUL&n^zADK% z<%UVBwic>jytDYYmCCJRJo3H^LdS8-NoPK2x6v5Kz{}5FlC>MtSuZt0Vp_ zo0zjxJ3a>j+-6!PIcJZhW{cY*WtwmoqUgr|9V+4W>I6&FgdGST$6#$-{bQYUiJG_{ zOxVRvjsFOL?me2j8f&W|mPW6(ooTG=Bx;gGrqIo%(0H@b2GRdBjCp+TDT&5BpE`~) z)cM&unwZdhL>rOh<(1{;=D~I&H#`g0FZm^v{^(DSbQ7 zdvRSOtUhAl(l882!rn_NLC{%-(B+eM$HN?@J|RuueGv1(>gWCz{}+H!%3^btT)qr<@;F=i@lp zH6^okiD3(K6f9;U;Z>no}4Cin5S)FVO^hVGjX#2q%bKCu%wjH6Z2|FLd*|XFp z#fgf>bb#OXV+?5PC~K%~2gCRtK2?~Q&mP=ug&Dy@+Fe}fUg2FHxGd??5u1}Y;ibsR z;~iB7UcHBQZN@m}OUH77EzZaCWq}4?A?+7zdX_iG7yeE7(dXfQftcASf%)t-LH9e;%&(7=fV_@8j|}-{G|02lwnwP*n9v`#hTJS}{GLo=}D5S3;?!PmNKlEhcEJ z9;{BTwK$~*u_AE|*}OlcLmgN5{d~&I3H6sumn@i%Gk)4vK^4|$h1!Z}o#4YL0XJiT zCzv4B>Vyuf;Hy=7KflVh*@&HLxQElX^|$XZsS{YBv{?8o#rnoS$)~438U2!>7}Nlq7if+>ZS5tWPMqGwuxWfX;>}htDf2ZExrXlgy4x#S=ZRhdi=R$;? zuakZmH0#TGI%$7L!hW0048QPB6 zKt%k##N&@pzYF&^Sd|N$ESM|!6~V=V><8qF3SJ4hE8{#Ajeb6 zxkK;)L5_jmJ6OMsP(vzZne-i3Tk~Vgufs( zU#}?tkAnJpDrmhIA@EFG2gd7d27zmZzD)4%1@9JoO7JBjey<90i$BW2M@fwVTvyWE z)Q^0l1*sd(@Uw*08)qS&J1{bQjo@{}>Ze+u6aKxh<)xA&XO3l^?lWG^ZVTMK09seK7Q}cPV3dE`VoIZF3w57_ipg=%mIGu zn%jT#-bMJ9Mc%&}Pvqdd=3$PawB@yOINuoIrZ^jbu3ERRH(q)JPNHxeKE-e`;Q~4hecSQ-LJKhd5*$K6`^pQ#F&tzb zZmO@(#p2$qug}C9CJuGUg$-;hmr=Pbv**mt?WoUfZE5RSlM6LyGHd(lc{RcOP~Pm& zm-3X;>m02zM{p}%FJ)rNi7C{lv<|nGt(6VZwTcs=uC=RRhpR%cPBhqTeZv}7(o_=G zwVmkm*w;XX22U?TA!B~JrLD2unm*R8T-h1B&>veTFQD;)9NvRRp}w5F59tc<;}xB)zngWfOqBh2q?HyHYUc__XJ661F5urkPp;Uw;?yh4On zKO@=SE{$^y#?3`o!g~q`mYelp8`yf(!ta-NC5XonW}S_zLV#c17!W~(Q6A-5c`M+z zY2a1-A~jq+I*0JB0AG*^#8D<)8-D(L3qWKdjQRTCFNeO*^g`Yx;IYn@iD>Rr6GQ#q zZ3x6`S6m+DGfy*o8omO&$J1uUI-l1DY4nGGk@uPThm^+h*j6!22fv@#-fiQ3H#36x z{!%3Jd2e%mefALgYoHlN$M{XashTo0zwPl^$uP!0Byj|`+bj6_>SyFC7&SlxUfVt5+lW1bK%FU0#&(RjKGgJ(&rxG z4&9Bh5Gy)7`(g0>v`mEKoEgni1^)r)X+fXt1L{XvzGsKfPYBIA{|bJ!19MB%+ljoo zrx$0+tcMQZ=%=sRl?)w7$ey5f7#&R3SM~Sp&e#Fo;H!JqN8Ev4bx)V44p0V zo`AERaH+4Hi*TGxG#YV7tA3tsWX_M?kMnRo>s13)YB}lyy*#zUb333fw+-bDZVm7( zC+G$4^7{IAsg6D;i29F0o263^5a+9%uG9w~V?4PEncRUN{fmH)4*6{d{kkvS3GT$X zCOh}K3crGlySz-$b8wbT&c^1Ls^T zoO8`{T0FEj^hmWH9_REovoG?j9JIy1@tQ!M!}5|B>EA#YuNTWfy|)|TSHk}a;^OsV z-Ec0=0_a^bEp$dd=*KxspH(?pCpQV{-4$qGZbI|OjU-l!u|I)qAj@S2mhHFnbfyku;fgp4pAve%}X%cz= zop~Xx6MTKonBa4J$3))PtM7xVEj-`Iqh4wNY11Q@CIzatxB;YDjx_pAwn%S3!uRU* zk=OT-cWmUjz2v9fCV3d=fiDw${&UcDf5ScIpbiD>TPsjs)Ct3qKKF{U*si*cs?WpO zEu#y3*Vtv5sPh;^I`FZ6mLF%!jD)V{E}Zu>!0;a+UY`Xs*R&@t*srFcY>wKoo9F59 z9nR^Pi!=JR?0x66&u+jOJ_DIsw+v8^IR*Np=&m1-pS;lFy@73*i*tq2BD@BD)K&cz zX$^+ddtB@I_hgmC?ZFMsz`-@eqW9LHn6b27>-2K#%MrAYYXh>1_ zVFG{-4QF1*FUROgcN7Ahhj4ql=71RQNd!Q_^4AQXF%2An{rGcovgzeC*vZwQ@F^!m zuc%&z;q`uiKd4@6lgMQ6Tnb4UaX(pcdd&OCMkW6@1Cwh|sHEh{_#ctX1<&N~WBwvOUPV%kba+A40=_Mb)e@b#IVl!2MYllwp zW8e&M`O_IZGKM7eH1&x#@grkmWQrddgQpqvH0_ZwD_Dm3kuhnc;z!1CZFBI*m;ev4 zkRxNXH_*p`rRPFk{K%N+nbk@LK|3!9Z5OKYhdYZhp&RUzrEEXz$WXx*T{~IPQ zB~@4LVnZGB$e8n4MtfuoyTrjq#+U&X-1O_tRi3d zAxIi?9s;>K>pbr;2Rcroo%6?Xp5y%Zdu)X&HD(j|Xx;0)tog{pMO4f8Q_J^b@Njh= z%A&km$g{}D_o?9X7j)LSfebe+ZM{A$BjgB-eu-Eaf4~s==9@sd6FF?V4N0U z&e*`&BXZ&fbL5E|W4JDTC4O$RE-fYS(+WzP%g`paf>Wcb=r-NMD%Pkm*CFaH_}xn# zHYgMDWDDo~3D79r%pj*~0&{XVlhs)~k<&o;GTn))v&K{M<&-=THb~07f~9fJL&MWE z<`YCy)7rgBOQ&Rh-7(T{^h>{ytXBHXTKaiL`YnUfZ`IPj3h7^_bRM#?fu(s&OQ+=h ztd>;x5nbWsV>h#mkMKe}XR}t1>U@`(T0Kf3&N;{52;hj6;y!LvKDR#zQImS|u{%F| z&ZM3K?99({P3oDW+$S69>q6`fZv=gaeJWdrA@*s^d7#SQqughYhR-?IG|qEX2z9DY zcpiDt9g7olUesY#D!2!McD8-2KAGe#b7cc6+s?90_y8+Um07ey5&peRq?5e+Q2&&q zQGw%-<><2@ZgLxZ<8TSxiH>d|whq6ZIp^z+<&JlBH>et?JIHuPH`tQ#Xrl?5-&v|V z87SRK%hVX|lYb*5cm_;iA4k_V#!2?^KPQLa`m1d#TC$>n87sI>Yr8 zhLad3zQRPkrl%c?Vi|93RtuWK5EE}hyLp$eC2=Ilu`OWIgtKgF856&ih3vEMM1B!? zChlj(PZ;ho6R%}Sh02`xWl9n%ed3#pd(u=kg%|4p#F^a_l>UbJg;((tHuCpx?B5X! z`N{fFJ=U$@l2BU|=EsgxIKjrngeyzS@~3m}{zy1B8=A-{X}OovNDbjo6%J(xJ?(vOJv5v*R8(w)9&=ua zR~hp1LS7a3zsKPMohiXvZ@smA<8ET4oY>|W4-493o|mWb}n--^)R^FHB`36Gd9j_6puaQ2lHSX-DL78%UFK!w(2J%t?sAl zK@0+~V8p<(VtswIBi4zHJ?mOqy4OZ8WaZ7WtzivlntyiYtNqAkD}Hz4cQ1a|zsj%K_ZRz!a;IgpjuzMdZNw(b*tWTS74}1KX=_rg z9eRIRY!!;VM|D6~LtV64?+zN(AA5{Ou-Si>v-57fKTn)r>#Yvm{`wG#z(#+!RkW_7 zU7~g!)oQ0bY_z5}Y=>>@Tf^N+}`tep1DVFS4($nu$D)n zFwAXa{rgWJv@LOb+iBE@Z5QRycY{@DV|Ah-qg}1Bm3jGdd~abO3??GYPfBZhrKQB1;|%= zKV(bJ(v?Y2B)0`d_p8MYxz(}O`K&a17LG>hsO#*CMOz2&8M!@l!yHbB8ZzQrbbp~8 zi?tfNzDaRG%sq=uvE@d#1?Qb&w)Qn!`ucUP%>_!_Aljxwg=r6AD{N|ns$<_~b068R z5Wi$Th5SQ&9`EX`_k9_sHymH*=RWFY^I^0LMhci~{I72J-(I)d|6sC@tBH6L`M>pE zoUPOjgz-7c=M?!cXH+K&jYp?;+c5?8JNrzVfk5&fj*pK|n}%{I@85j^);oU)AJ0?d zQ`tJpcLoCd^*C)ZR%1|j)&mdlcs;7&w`t&2C8O{y-xpxL6^Qf8yY?hgFUn*6Y`c6F zex@;#0O*uug77&jkFRd<$922Ox7>tjZhJjiK>72%3Hh!-81uz9qIkX+!*6B7tES;& z{As=bD{l?r{PGS!-Zq3$-ck73dTfE;ms~aA0~N2ObvEt_1o-9Mi^&q(gYwXnae24F zZ_~i5{tohj^oX-@T$=OCdl>TAgp^190$VE;PSx{c z7M1m9o7j5@UX>YC>H=FR&^|AtZje+4{z7vnpNjVEp6{}G-YKAP>rwKU@#br(GLUB;#-ZR5AYW8Y_NJZT$G zd3>L-wDl4H7P{&$`!Zy|eSHZ1t0DBKLul%vSjY5Su($7L)O9&^NPNi<+U{A)6^27h zZ>A>fSnHqZ`4af77tJi4lcIy?Lwa6nXSJFbd;`FI_fYd_?0-AVjP~FerrG56i*8^m z?~6nA--mm5P0+bNNPYegdh#6Sv!t%BD3tpc^4K2Yzs z3fv%kTLtxgs|e@92J_MS`#`_X@rw z_`1Y%ot6CWNcgV>N1^|*d=rR>=fW`YL-v+@eB>XnP?Sj7$>?cC*9>HHp`0oXiaF4S*#|Q=m=Ljwk zJYTS$2zkwdt%6^d`1OKY1aA~v&HD}M=xFDl9R`mSp*hYloZ|~ID2Sm-(;QzIK1Xn_ z;3B~)K|M}o8#&AmL!5F!Gd<}-p_z{Kc|xxcx?5<@e;9w2&|8Jp;}PWb3H`9dKPvRw zLiY>(d!Y{qJsP?rM=GFS09?hWgN!Qpm8DCsp9Z<`*x1@$*X>)3Oo-HLUY~yz^g za79>$CuDHs%RjKGz$#EaCT4K~x^z+%<3D)wgNF7VE^dF}1tP9!h`|b9%93CJacXU0 zc`-~^U^H<^3*!Ozwea?96Y`&JZ=np^qd%eFTtf#&D zM%v-e7Q%zg=MJ`k@LyTVAE}M%jF;|WM3m)Bv>X`BY2Kr zonW(|9(0lJLZQuPf|d2FI?NO2aQf!?b0P&t`n+joXU63<_un*}TEii{dN*{ep*N{U zI+3^W+Va{S1$|3gg$Yiu+UQtACz7^&o?6jQ{FBcpzd~eSDzxs5&Sa8LGcRGs!x4$f zgUN}ig(+#y5)3b?iCZj=q+Vk&3ZG^)8`a0Chv`Q~q0f#mvk~sx%t$_r0!jKY=>B3R zLfFZv+m{D$k^_8Xf|5Wq0UnHj*9a+rO8{H+?LalME`JSD%d4Pm&Yt-iIVA?Cl&&^%q;4GpnIs9NX? zCCU~3X$}+<3TBj+m4&CfqGZ5v*RKyi+B!C-z87@>XLXn1H+a|RU=EJb9W>29+a>6y zK=S0^=MRrR`wu&H>1w$MCuEwhc7N^+##i{i_GIQF92#mSXMe334X=&NXn2SCXEa!1 z`OD2|4s9B0R)~DIqa8gf+v-*&+LKxfQ8_*HudrbeDnH!($lu-cXahbD6u3d+Pd<(? ze#bbvq4T3d@v&RmG)zanZ_^{r#toex{Ws!yM_Fh2zKj6>Q#61_=njO9!Ht5ytjSjv-2aCcO!l_4P~U_p&6t{oQ>mN7k+treC5;6 z55!X*uaQjyucA8MIC{j{xIWPIa}Gi`4}2qSBxrj1yGIb9T`3x3xcTM7h=qs_VN)By zH^~w}D|+bs@;2~2GYlX3&G2g?-$dp3or21)ye>#K?ELb5G}3sJME~1-eL)9>|1IX1 zyyk;0u~c5dm1R37av%P?23us%dHPGDpFT$0GBczAKYnN6rVuiJ<@W-;X3iNSdGtC4 zZc0Al4tY)&I-P!o0#6|ykg|z--55Au=wiVt!DWKHH_4BuyvA0+F2RcgFA>}#_#GlT dhn|!HpOEmEh$!@SiRZoRqCc?z|Ecwv{|7^Je? tx_thread_new_time_slice, new_time_slice); TX_EL_END_FILTER +#define TX_EL_THREAD_TERMINATE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_TERMINATE, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_SLEEP_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SLEEP, timer_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_SUSPEND_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_SUSPEND, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_RELINQUISH_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_RELINQUISH); TX_EL_END_FILTER +#define TX_EL_THREAD_RESUME_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESUME, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PRIORITY_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PRIORITY_CHANGE, thread_ptr, thread_ptr -> tx_thread_priority, new_priority); TX_EL_END_FILTER +#define TX_EL_THREAD_PREEMPTION_CHANGE_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_THREAD_PREEMPTION_CHANGE, thread_ptr, thread_ptr -> tx_thread_preempt_threshold, new_threshold); TX_EL_END_FILTER +#define TX_EL_THREAD_WAIT_ABORT_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_WAIT_ABORT, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_ENTRY_EXIT_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_THREAD_ENTRY_EXIT_NOTIFY, thread_ptr, thread_entry_exit_notify); TX_EL_END_FILTER +#define TX_EL_THREAD_RESET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_RESET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_PERFORMANCE_INFO_GET, thread_ptr); TX_EL_END_FILTER +#define TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_THREAD_STACK_ERROR_NOTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_THREAD_STACK_ERROR_NOTIFY, stack_error_handler); TX_EL_END_FILTER +#define TX_EL_TIME_SET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_SET, new_time); TX_EL_END_FILTER +#define TX_EL_TIME_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIME_GET, _tx_timer_system_clock); TX_EL_END_FILTER +#define TX_EL_TIMER_DELETE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DELETE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_CREATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_TIMER_CREATE, timer_ptr, initial_ticks, reschedule_ticks, auto_activate); TX_EL_END_FILTER +#define TX_EL_TIMER_CHANGE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_TIMER_CHANGE, timer_ptr, initial_ticks, reschedule_ticks); TX_EL_END_FILTER +#define TX_EL_THREAD_IDENTIFY_INSERT TX_EL_NO_THREAD_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_THREAD_IDENTIFY); TX_EL_END_FILTER +#define TX_EL_TIMER_DEACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_DEACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_ACTIVATE_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_ACTIVATE, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_TIMER_PERFORMANCE_INFO_GET, timer_ptr); TX_EL_END_FILTER +#define TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_TIMER_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_TIMER_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_GET, semaphore_ptr, semaphore_ptr -> tx_semaphore_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_DELETE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_DELETE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CREATE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_CREATE, semaphore_ptr, initial_count); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PRIORITIZE_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PRIORITIZE, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_CEILING_PUT_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_SEMAPHORE_CEILING_PUT, semaphore_ptr, semaphore_ptr -> tx_semaphore_count, ceiling); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_SEMAPHORE_PERFORMANCE_INFO_GET, semaphore_ptr); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_SEMAPHORE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_SEMAPHORE_PUT_NOTIFY_INSERT TX_EL_NO_SEMAPHORE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_SEMAPHORE_PUT_NOTIFY, semaphore_ptr, semaphore_put_notify); TX_EL_END_FILTER +#define TX_EL_QUEUE_FRONT_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_FRONT_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND, queue_ptr, source_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_RECEIVE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_RECEIVE, queue_ptr, destination_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_FLUSH_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_FLUSH, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_DELETE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_DELETE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_CREATE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_QUEUE_CREATE, queue_ptr, queue_start, queue_size, message_size); TX_EL_END_FILTER +#define TX_EL_QUEUE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PRIORITIZE_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PRIORITIZE, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_QUEUE_PERFORMANCE_INFO_GET, queue_ptr); TX_EL_END_FILTER +#define TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_QUEUE_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_QUEUE_SEND_NOTIFY_INSERT TX_EL_NO_QUEUE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_QUEUE_SEND_NOTIFY, queue_ptr, queue_send_notify); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_EVENT_FLAGS_GET, group_ptr, requested_flags, get_option); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_DELETE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_DELETE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_CREATE_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_CREATE, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_EVENT_FLAGS_PERFORMANCE_INFO_GET, group_ptr); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_EVENT_FLAGS_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_EVENT_FLAGS_SET_NOTIFY_INSERT TX_EL_NO_EVENT_FLAG_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_EVENT_FLAGS_SET_NOTIFY, group_ptr, events_set_notify); TX_EL_END_FILTER +#define TX_EL_BYTE_RELEASE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BYTE_RELEASE, pool_ptr, memory_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_DELETE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_CREATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_POOL_CREATE, pool_ptr, pool_start, pool_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PRIORITIZE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_ALLOCATE_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_BYTE_ALLOCATE, pool_ptr, memory_ptr, memory_size); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BYTE_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BYTE_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BYTE_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_BLOCK_RELEASE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_RELEASE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_DELETE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_DELETE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_CREATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(TX_EL_BLOCK_POOL_CREATE, pool_ptr, pool_start, pool_size, block_size); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PRIORITIZE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PRIORITIZE, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_ALLOCATE_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_BLOCK_ALLOCATE, pool_ptr, block_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_BLOCK_POOL_PERFORMANCE_INFO_GET, pool_ptr); TX_EL_END_FILTER +#define TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_BLOCK_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_BLOCK_POOL_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER +#define TX_EL_MUTEX_CREATE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_MUTEX_CREATE, mutex_ptr, inherit); TX_EL_END_FILTER +#define TX_EL_MUTEX_DELETE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_DELETE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_GET, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PRIORITIZE_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PRIORITIZE, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PUT_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(TX_EL_MUTEX_PUT, mutex_ptr, mutex_ptr -> tx_mutex_owner, mutex_ptr -> tx_mutex_ownership_count); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(TX_EL_MUTEX_PERFORMANCE_INFO_GET, mutex_ptr); TX_EL_END_FILTER +#define TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET_INSERT TX_EL_NO_MUTEX_EVENTS TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(TX_EL_MUTEX_PERFORMANCE_SYSTEM_INFO_GET); TX_EL_END_FILTER + + +#endif + + +/* Define Event Log function prototypes. */ + +VOID _tx_el_initialize(VOID); +UINT _tx_el_thread_register(TX_THREAD *thread_ptr); +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr); +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4); +VOID _tx_el_thread_running(TX_THREAD *thread_ptr); +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr); +VOID _tx_el_interrupt(UINT interrupt_number); +VOID _tx_el_interrupt_end(UINT interrupt_number); +VOID _tx_el_interrupt_control_call(void); +VOID _tx_el_event_log_on(void); +VOID _tx_el_event_log_off(void); +VOID _tx_el_event_filter_set(UINT filter); + + +/* Define macros that are used inside the ThreadX source code. + If event logging is disabled, these macros will be defined + as white space. */ + +#ifdef TX_ENABLE_EVENT_LOGGING +#ifndef TX_NO_EVENT_INFO +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =\ + (ULONG) e;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =\ + (ULONG) d;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) =\ + (ULONG) c;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) =\ + (ULONG) b;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREADX_CALL; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) a; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) _tx_thread_current_ptr;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + } +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) \ + { \ + UCHAR *entry_ptr; \ + ULONG upper_tbu; \ + TX_EL_NO_STATUS_EVENTS \ + entry_ptr = *_tx_el_current_event; \ + *((unsigned short *) entry_ptr) = TX_EL_THREAD_STATUS_CHANGE; \ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = (unsigned short) b; \ + do { \ + upper_tbu = read_tbu(); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = upper_tbu; \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) =\ + (ULONG) read_tbl();\ + } while (upper_tbu != read_tbu()); \ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) =\ + (ULONG) a;\ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE;\ + if (entry_ptr >= _tx_el_event_area_end) \ + {\ + entry_ptr = _tx_el_event_area_start;\ + }\ + *_tx_el_current_event = entry_ptr;\ + TX_EL_END_FILTER \ + } +#define TX_EL_THREAD_REGISTER(a) \ + _tx_el_thread_register(a); +#define TX_EL_THREAD_UNREGISTER(a) \ + _tx_el_thread_unregister(a); +#define TX_EL_INITIALIZE _tx_el_initialize(); +#endif +#else +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO4(a, b, c, d, e) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO3(a, b, c, d) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(a, b, c) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO1(a, b) +#define TX_EL_KERNEL_CALL_EVENT_INSERT_INFO0(a) +#define TX_EL_THREAD_STATUS_CHANGE_INSERT(a, b) +#define TX_EL_THREAD_REGISTER(a) +#define TX_EL_THREAD_UNREGISTER(a) +#define TX_EL_INITIALIZE +#endif + +#endif + diff --git a/ports/cortex_r7/ghs/inc/tx_ghs.h b/ports/cortex_r7/ghs/inc/tx_ghs.h new file mode 100644 index 00000000..ca976916 --- /dev/null +++ b/ports/cortex_r7/ghs/inc/tx_ghs.h @@ -0,0 +1,77 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#ifndef _TX_GHS_H_ +#define _TX_GHS_H_ + +#include +#include +#include +#include + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +extern void *__ghs_GetThreadLocalStorageItem(int specifier); + +/* Thread-local storage routines for Green Hills releases 5.x and beyond. + The following specifiers are used when calling + __ghs_GetThreadLocalStorageItem. + + If __ghs_GetThreadLocalStorageItem is customized to + return a per-thread errno value, define the preprocessor symbol + USE_THREAD_LOCAL_ERRNO in ind_errn.c. + */ + +enum __ghs_ThreadLocalStorage_specifier { + __ghs_TLS_asctime_buff, + __ghs_TLS_tmpnam_space, + __ghs_TLS_strtok_saved_pos, + __ghs_TLS_Errno, + __ghs_TLS_gmtime_temp, + __ghs_TLS___eh_globals, + __ghs_TLS_SignalHandlers +}; +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ +typedef void (*SignalHandler)(int); + +typedef struct +{ + int Errno; /* errno. */ + SignalHandler SignalHandlers[_SIGMAX]; /* signal() buffer. */ + char tmpnam_space[L_tmpnam]; /* tmpnam(NULL) buffer. */ + char asctime_buff[30]; /* . */ + char *strtok_saved_pos; /* strtok() position. */ + struct tm gmtime_temp; /* gmtime() and localtime() buffer. */ + void *__eh_globals; /* Pointer for C++ exception handling. */ +} ThreadLocalStorage; + +ThreadLocalStorage *GetThreadLocalStorage(void); +#endif + + +void __ghsLock(void); +void __ghsUnlock(void); + +int __ghs_SaveSignalContext(jmp_buf); +void __ghs_RestoreSignalContext(jmp_buf); + +/* prototypes for FILE lock routines. */ +void __ghs_flock_file(void *); +void __ghs_funlock_file(void *); +int __ghs_ftrylock_file(void *); +void __ghs_flock_create(void **); +void __ghs_flock_destroy(void *); + +/* prototype for GHS/ThreadX error shell checking. */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal); + +#endif /* _TX_GHS_H_ */ diff --git a/ports/cortex_r7/green/inc/tx_port.h b/ports/cortex_r7/ghs/inc/tx_port.h similarity index 91% rename from ports/cortex_r7/green/inc/tx_port.h rename to ports/cortex_r7/ghs/inc/tx_port.h index 88827e80..6518d89e 100644 --- a/ports/cortex_r7/green/inc/tx_port.h +++ b/ports/cortex_r7/ghs/inc/tx_port.h @@ -12,7 +12,7 @@ /**************************************************************************/ /**************************************************************************/ -/** */ +/** */ /** ThreadX Component */ /** */ /** Port Specific */ @@ -21,36 +21,36 @@ /**************************************************************************/ -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h Cortex-R7/Green Hills */ -/* 6.1.6 */ +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-R7/GHS */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -63,7 +63,7 @@ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" @@ -78,7 +78,7 @@ #include "tx_ghs.h" -/* Define ThreadX basic types for this port. */ +/* Define ThreadX basic types for this port. */ #define VOID void typedef char CHAR; @@ -114,12 +114,12 @@ typedef unsigned short USHORT; #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX ARM port. */ +/* Define various constants for the ThreadX ARM port. */ #ifdef TX_ENABLE_FIQ_SUPPORT #define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ @@ -134,13 +134,13 @@ typedef unsigned short USHORT; /* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ -#define TX_EL_TICKS_PER_SECOND 1000000 +#define TX_EL_TICKS_PER_SECOND 1000000 /* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply - simulate the time-stamp source with a counter. */ + simulate the time-stamp source with a counter. */ -#define read_tbu() _tx_el_time_base_upper -#define read_tbl() ++_tx_el_time_base_lower +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower /* Define the port specific options for the _tx_build_options variable. This variable indicates @@ -174,7 +174,7 @@ typedef unsigned short USHORT; #define TX_INLINE_INITIALIZATION -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -186,16 +186,16 @@ typedef unsigned short USHORT; /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 #define TX_THREAD_EXTENSION_2 ULONG tx_thread_vfp_enable; \ VOID *tx_thread_eh_globals; \ int Errno; /* errno. */ \ char *strtok_saved_pos; /* strtok() position. */ -#define TX_THREAD_EXTENSION_3 +#define TX_THREAD_EXTENSION_3 /* Define the port extensions of the remaining ThreadX objects. */ @@ -209,11 +209,11 @@ typedef unsigned short USHORT; #define TX_TIMER_EXTENSION -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION #endif @@ -243,7 +243,7 @@ typedef unsigned short USHORT; extern void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr); \ __tx_cpp_exception_cleanup(thread_ptr); \ } -#else +#else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) \ { \ #pragma weak __cpp_exception_cleanup \ @@ -281,18 +281,18 @@ typedef unsigned short USHORT; #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the lowest bit set. */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) m = m & ((ULONG) (-((LONG) m))); \ b = __CLZ32(m); \ - b = 31 - b; + b = 31 - b; -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value present prior to the disable macro. In most cases, the save area macro is used to define a local function save area for the disable and restore macros. */ @@ -302,7 +302,7 @@ typedef unsigned short USHORT; unsigned int _tx_thread_interrupt_disable(void); void _tx_thread_interrupt_restore(unsigned int new_posture); -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); @@ -310,7 +310,7 @@ void _tx_thread_interrupt_restore(unsigned int new_po #else -#define TX_INTERRUPT_SAVE_AREA register INT interrupt_save; +#define TX_INTERRUPT_SAVE_AREA register int interrupt_save; #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER >= 350) @@ -349,7 +349,7 @@ asm int disable_ints(void) MSR CPSR_c,r1 #else #ifdef TX_ENABLE_FIQ_SUPPORT - CPSID if + CPSID if #else CPSID i #endif @@ -395,7 +395,7 @@ void tx_thread_vfp_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-R7/Green Hills Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-R7/Green Hills Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports/cortex_r7/green/readme_threadx.txt b/ports/cortex_r7/ghs/readme_threadx.txt similarity index 100% rename from ports/cortex_r7/green/readme_threadx.txt rename to ports/cortex_r7/ghs/readme_threadx.txt diff --git a/ports/cortex_r7/ghs/src/tx_el.c b/ports/cortex_r7/ghs/src/tx_el.c new file mode 100644 index 00000000..d8f056d7 --- /dev/null +++ b/ports/cortex_r7/ghs/src/tx_el.c @@ -0,0 +1,1165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX/GHS Event Log (EL) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE +#define TX_EL_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_el.h" +#include "string.h" + + +/* Define global variables used to manage the event pool. */ + +UCHAR *_tx_el_tni_start; +UCHAR **_tx_el_current_event; +UCHAR *_tx_el_event_area_start; +UCHAR *_tx_el_event_area_end; +UINT _tx_el_maximum_events; +ULONG _tx_el_total_events; +UINT _tx_el_event_filter; +ULONG _tx_el_time_base_upper; +ULONG _tx_el_time_base_lower; + +extern char __ghsbegin_eventlog[]; +extern char __ghsend_eventlog[]; + +extern TX_THREAD *_tx_thread_current_ptr; +UINT _tx_thread_interrupt_control(UINT new_posture); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_initialize PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates the Event Log (in the format dictated by the */ +/* GHS Event Analyzer) and sets up various information for subsequent */ +/* operation. The start and end of the Event Log is determined by the */ +/* .eventlog section in the linker control file. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_initialize(VOID) +{ + +UCHAR *work_ptr; +UCHAR *read_ptr; +ULONG event_log_size; +UCHAR *end_ptr; +UINT i; + + + /* Clear total event counter. */ + _tx_el_total_events = 0; + + /* Clear event filter. */ + _tx_el_event_filter = 0; + + /* First, pickup the starting and ending address of the Event Log memory. */ + work_ptr = (unsigned char *) __ghsbegin_eventlog; + end_ptr = (unsigned char *) __ghsend_eventlog; + + /* Calculate the event log size. */ + event_log_size = end_ptr - work_ptr; + + /* Subtract off the number of bytes in the header and the TNI area. */ + event_log_size = event_log_size - (TX_EL_HEADER_SIZE + + (TX_EL_TNI_ENTRY_SIZE * TX_EL_TNIS)); + + /* Make sure the event log is evenly divisible by the event size. */ + event_log_size = (event_log_size/TX_EL_EVENT_SIZE) * TX_EL_EVENT_SIZE; + + /* Build the Event Log header. */ + + /* Setup the Event Log Version ID. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_VERSION_ID; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the TNIS (number of thread names) field. */ + *((unsigned short *) work_ptr) = (unsigned short) TX_EL_TNIS; + work_ptr = work_ptr + sizeof(unsigned short); + + /* Setup the EVPS (event pool size) field. */ + *((ULONG *) work_ptr) = event_log_size; + work_ptr = work_ptr + sizeof(ULONG); + + /* Remember the maximum number of events. */ + _tx_el_maximum_events = event_log_size/TX_EL_EVENT_SIZE; + + /* Setup max_events field. */ + *((ULONG *) work_ptr) = _tx_el_maximum_events; + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup the evploc (location of event pool). */ + *((ULONG *) work_ptr) = (ULONG) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Save the current event pointer. */ + _tx_el_current_event = (UCHAR **) work_ptr; + + /* Setup event_ptr (pointer to oldest event) field to the start + of the event pool. */ + *_tx_el_current_event = (UCHAR *) (((ULONG) __ghsbegin_eventlog) + TX_EL_HEADER_SIZE + + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = work_ptr + sizeof(ULONG); + + /* Setup tbfreq (the number of ticks in a second) field. */ + *((ULONG *) work_ptr) = TX_EL_TICKS_PER_SECOND; + work_ptr = work_ptr + sizeof(ULONG); + + /* At this point we are pointing at the Thread Name Information (TNI) array. */ + + /* Remember the start of this for future updates. */ + _tx_el_tni_start = work_ptr; + + /* Clear the entire TNI array, this is the initial setting. */ + end_ptr = work_ptr + (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE); + memset((void *)work_ptr, 0, (TX_EL_TNIS * TX_EL_TNI_ENTRY_SIZE)); + work_ptr = end_ptr; + + /* At this point, we are pointing at the actual Event Entry area. */ + + /* Remember the start of the actual event log area. */ + _tx_el_event_area_start = work_ptr; + + /* Clear the entire Event area. */ + end_ptr = work_ptr + event_log_size; + memset((void *)work_ptr, 0, event_log_size); + work_ptr = end_ptr; + + /* Save the end pointer for later use. */ + _tx_el_event_area_end = work_ptr; + + /* Setup an entry to resolve all activities from initialization and from + an idle system. */ + work_ptr = _tx_el_tni_start; + read_ptr = (UCHAR *) "Initialization/System Idle"; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID to NULL. */ + *((ULONG *) (_tx_el_tni_start + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) TX_NULL; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (_tx_el_tni_start + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Clear the time base global variables. */ + _tx_el_time_base_upper = 0; + _tx_el_time_base_lower = 0; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_register PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_register(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT i; + + + /* First of all, search for a free slot in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is available. */ + if (*(entry_ptr + TX_EL_TNI_VALID_OFFSET) == TX_EL_INVALID_ENTRY) + break; + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Check to see if there were no more valid entries. */ + if (i >= TX_EL_TNIS) + return(TX_EL_NO_MORE_TNI_ROOM); + + /* Otherwise, we have room in the TNI and a valid record. */ + + /* Setup the thread's name. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + i = 0; + while ((i < TX_EL_TNI_NAME_SIZE) && (*read_ptr)) + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + + /* Increment the character count. */ + i++; + } + + /* Determine if a NULL needs to be inserted. */ + if (i < TX_EL_TNI_NAME_SIZE) + { + + /* Yes, insert a NULL into the event log string. */ + *work_ptr = (unsigned char) 0; + } + + /* Setup the thread ID. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_ID_OFFSET)) = (ULONG) thread_ptr; + + /* Setup the thread priority. */ + *((ULONG *) (entry_ptr + TX_EL_TNI_THREAD_PRIORITY_OFF)) = (ULONG) thread_ptr -> tx_thread_priority; + + /* Set the valid field to indicate the entry is complete. */ + *((UCHAR *) (entry_ptr + TX_EL_TNI_VALID_OFFSET)) = (ULONG) TX_EL_VALID_ENTRY; + + /* Thread name has been registered. */ + return(TX_SUCCESS); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_unregister PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function unregisters a thread in the event log for future */ +/* display purposes. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control block */ +/* */ +/* OUTPUT */ +/* */ +/* TX_SUCCESS Thread was placed in TNI area */ +/* TX_ERROR No more room in the TNI area */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create ThreadX thread create function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_thread_unregister(TX_THREAD *thread_ptr) +{ + +UCHAR *entry_ptr; +UCHAR *work_ptr; +UCHAR *read_ptr; +UINT found; +UINT i, j; + + + /* First of all, search for a match in the TNI area. */ + entry_ptr = _tx_el_tni_start; + i = 0; + while (i < TX_EL_TNIS) + { + + /* Determine if this entry is a match. */ + work_ptr = entry_ptr; + read_ptr = (UCHAR *) thread_ptr -> tx_thread_name; + found = TX_TRUE; + j = 0; + do + { + + /* Determine if this character is the same. */ + if (*work_ptr != *read_ptr) + { + + /* Set found to false and fall out of the loop. */ + found = TX_FALSE; + break; + } + else if (*work_ptr == 0) + { + + /* Null terminated, just break the loop. */ + break; + } + else + { + + /* Copy a character of thread's name into TNI area of log. */ + *work_ptr++ = *read_ptr++; + } + + /* Increment the character count. */ + j++; + + } while(j < TX_EL_TNIS); + + + /* Was a match found? */ + if (found) + { + + /* Yes, mark the entry as available now. */ + *(entry_ptr + TX_EL_TNI_VALID_OFFSET) = TX_EL_INVALID_ENTRY; + + /* Get out of the loop! */ + break; + } + + /* Otherwise, increment the associated pointers and indices. */ + i++; + entry_ptr = entry_ptr + TX_EL_TNI_ENTRY_SIZE; + } + + /* Determine status to return. */ + if (found) + return(TX_SUCCESS); + else + return(TX_EL_NAME_NOT_FOUND); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_user_event_insert PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a user event into the event log. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* sub_type Event subtype for kernel call */ +/* info_1 First information field */ +/* info_2 Second information field */ +/* info_3 Third information field */ +/* info_4 Fourth information field */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_user_event_insert(UINT sub_type, ULONG info_1, ULONG info_2, + ULONG info_3, ULONG info_4) +{ + +TX_INTERRUPT_SAVE_AREA + +UINT upper_tb; +UCHAR *entry_ptr; + + /* Disable interrupts. */ + TX_DISABLE + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_USER_EVENT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) sub_type; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) info_1; + + /* Store the second info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_2_OFFSET)) = + (ULONG) info_2; + + /* Store the third info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) = + (ULONG) info_3; + + /* Store the fourth info field. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) = + (ULONG) info_4; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + /* Restore interrupts. */ + TX_RESTORE +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_running PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread change event into the event */ +/* log, which indicates that a context switch is taking place. */ +/* If the event log is full, the oldest event is overwritten. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_schedule ThreadX scheduler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_running(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) 0; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) thread_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_thread_preempted PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts a thread preempted event into the event */ +/* log, which indicates that an interrupt occurred that made a higher */ +/* priority thread ready for execution. In this case, the previously */ +/* executing thread has an event entered to indicate it is no longer */ +/* running. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread being */ +/* scheduled */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_context_restore ThreadX context restore */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_thread_preempted(TX_THREAD *thread_ptr) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_STATUS_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_THREAD_STATUS_CHANGE; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_READY; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt event into the log, which */ +/* indicates the start of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_INTERRUPT_SUB_TYPE; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_end PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function inserts an interrupt end event into the log, which */ +/* indicates the end of interrupt processing for the specific */ +/* */ +/* INPUT */ +/* */ +/* interrupt_number Interrupt number supplied by */ +/* ISR */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISR processing */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_interrupt_end(UINT interrupt_number) +{ + +UINT upper_tb; +UCHAR *entry_ptr; + + + TX_EL_NO_INTERRUPT_EVENTS + + /* Increment total event counter. */ + _tx_el_total_events++; + + /* Setup working entry pointer first. */ + entry_ptr = *_tx_el_current_event; + + /* Store the event type. */ + *((unsigned short *) entry_ptr) = (unsigned short) TX_EL_INTERRUPT; + + /* Store the event subtype. */ + *((unsigned short *) (entry_ptr + TX_EL_EVENT_SUBTYPE_OFFSET)) = + (unsigned short) TX_EL_END_OF_INTERRUPT; + + /* Get time stamp. */ + do + { + + /* Pickup the upper tb. */ + upper_tb = (ULONG) read_tbu(); + + /* Store the upper time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) = + (ULONG) upper_tb; + + /* Store the lower time stamp. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) = + (ULONG) read_tbl(); + } while (upper_tb != (ULONG) read_tbu()); + + /* Store the current thread. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_THREAD_OFFSET)) = + (ULONG) _tx_thread_current_ptr; + + /* Store the first info word. */ + *((ULONG *) (entry_ptr + TX_EL_EVENT_INFO_1_OFFSET)) = + (ULONG) interrupt_number; + + /* Now move the current event log pointer. */ + entry_ptr = entry_ptr + TX_EL_EVENT_SIZE; + + /* Check for a wraparound condition. */ + if (entry_ptr >= _tx_el_event_area_end) + { + + /* Yes, we have wrapped around to the end of the event area. + Start back at the top! */ + entry_ptr = _tx_el_event_area_start; + } + + /* Write the entry pointer back into the header. */ + *_tx_el_current_event = entry_ptr; + + TX_EL_END_FILTER +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_interrupt_control PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function remaps the tx_interrupt_control service call so that */ +/* it can be tracked in the event log. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt posture */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_interrupt_control Interrupt control service */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX services */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +UINT _tx_el_interrupt_control(UINT new_posture) +{ + +TX_INTERRUPT_SAVE_AREA +UINT old_posture; + + + TX_EL_NO_INTERRUPT_EVENTS + + TX_DISABLE + TX_EL_KERNEL_CALL_EVENT_INSERT_INFO2(TX_EL_INTERRUPT_CONTROL, _tx_thread_current_ptr, new_posture) + TX_RESTORE + + TX_EL_END_FILTER + + old_posture = _tx_thread_interrupt_control(new_posture); + return(old_posture); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_on PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables all event filters. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_on(void) +{ + + /* Disable all event filters. */ + _tx_el_event_filter = TX_EL_ENABLE_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_off PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets all event filters, thereby turning event */ +/* logging off. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_log_off(void) +{ + + /* Set all event filters. */ + _tx_el_event_filter = TX_EL_FILTER_ALL_EVENTS; +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_el_event_log_set PORTABLE C */ +/* 6.1 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets the events filters specified by the user. */ +/* */ +/* INPUT */ +/* */ +/* filter Events to filter */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +VOID _tx_el_event_filter_set(UINT filter) +{ + + /* Apply the user event filter. */ + _tx_el_event_filter = filter; +} + diff --git a/ports/cortex_r7/ghs/src/tx_ghs.c b/ports/cortex_r7/ghs/src/tx_ghs.c new file mode 100644 index 00000000..30b8054e --- /dev/null +++ b/ports/cortex_r7/ghs/src/tx_ghs.c @@ -0,0 +1,485 @@ +/* + * ThreadX C/C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" +#include +#include + +/* Allow these routines to access the following ThreadX global variables. */ +extern ULONG _tx_thread_created_count; +extern TX_THREAD *_tx_thread_created_ptr; +extern TX_THREAD *_tx_thread_current_ptr; + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +/* Thread-local storage routines for Green Hills releases 5.x and above. */ +/* + Thread-Local (Per-Thread) Library Data Retrieval + ================================================ + + __ghs_ThreadLocalStorage_specifier defines all library data items + that the Green Hills libraries allow to be allocated per-thread. + + An implementation can choose which of these data items to allocate + for each thread. For example, an implementation may choose to + allocate an errno value for each thread, but not the strtok_saved_pos + pointer. The application could then use strtok_r instead of strtok for + correct operation. + + To add per-thread library data, define one of the + TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item + or items in each thread control block TX_THREAD. + + If C++ with exceptions is being used, the __eh_globals entry must be + allocated for each thread. This is typically done by default using + TX_THREAD_EXTENSION_1 in tx_port.h. + + If __ghs_GetThreadLocalStorageItem is customized to return a + per-thread errno value, you should also: + + * Customize the System Library for your project + * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in + src/libsys/ind_errn.c + + If you customize the System Library, you should remove ind_thrd.c + from the libsys.gpj subproject. + + */ + +/* Provide global __eh_globals value to support C++ exception handling + outside a thread context. This name also forces this module to be + included in the linked program instead of the ind_thrd.o module from + the System Library libsys.a. + */ +static void *__eh_globals; + +#pragma ghs startnomisra +void *__ghs_GetThreadLocalStorageItem(int specifier) +{ + void *ptlsitem = (void *)0; + switch (specifier) { + case (int)__ghs_TLS_Errno: + /* Set ptslsitem to the address of the per-thread errno value. + The per-thread errno value should have the type int. + + If returning a per-thread errno value, follow the steps + above. + + This item is used by numerous library functions. + */ + break; + case (int)__ghs_TLS_SignalHandlers: + /* Set ptslsitem to the address of the per-thread SignalHandlers + array. The per-thread SignalHandlers array should have the + array type as in the following declaration: + SignalHandler SignalHandlers[_SIGMAX]; + The SignalHandler type and _SIGMAX constant are defined in + ind_thrd.h. + + This item is used by the library functions signal() and + raise(). + */ + break; + case (int)__ghs_TLS_asctime_buff: + /* Set ptslsitem to the address of the per-thread asctime_buff + array. The per-thread asctime_buff array should have the + array type as in the following declaration: + char asctime_buff[30]; + + This item is used by the library functions asctime() and + ctime(). The library provides asctime_r() and ctime_r(), + inherently thread-safe versions of these functions. + */ + break; + case (int)__ghs_TLS_tmpnam_space: + /* Set ptslsitem to the address of the per-thread tmpnam_space + array. The per-thread tmpnam_space array should have the + array type as in the following declaration: + char tmpnam_space[L_tmpnam]; + The constant is defined in + + This item is used by the library function tmpnam() when + passed NULL. The library provides tmpnam_r(), an + inherently thread-safe version of tmpnam(). + */ + break; + case (int)__ghs_TLS_strtok_saved_pos: + /* Set ptslsitem to the address of the per-thread + strtok_saved_pos pointer. The per-thread strtok_saved_pos + pointer should have the type "char *". + + This item is used by the library function strtok(). + The library provides strtok_r(), an inherently thread-safe + version of strtok(). + */ + break; + case (int)__ghs_TLS_gmtime_temp: + /* Set ptslsitem to the address of the per-thread gmtime_temp + value. The per-thread gmtime_temp value should have the + type "struct tm" defined in time.h, included by indos.h. + + This item is used by the library functions gmtime() and + localtime(). The library provides gmtime_r() and + localtime_r(), inherently thread-safe versions of these + functions. + */ + break; + case (int)__ghs_TLS___eh_globals: + /* Set ptslsitem to the address of the per-thread __eh_globals + value. The per-thread __eh_globals value should have the + type "void *". + + This item is used by C++ exception handling. + */ + if (_tx_thread_current_ptr) + ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + ptlsitem = (void *)&__eh_globals; + break; + } + return ptlsitem; +} +#pragma ghs endnomisra +#else +/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */ + +/* + * ThreadX C and C++ thread-safe library support routines. + * + * This implementation merely tries to guarantee thread safety within + * individual C library calls such as malloc() and free(), but it does + * not attempt to solve the problems associated with the following + * multithreaded issues: + * + * 1. Use of errno. This can be made thread-safe by adding errno + * to TX_THREAD_PORT_EXTENSION and using that within a modified + * version of libsys/ind_errno.c. + * + * 2. Thread safety ACROSS library calls. Certain C library calls either + * return pointers to statically-allocated data structures or maintain + * state across calls. These include strtok(), asctime(), gmtime(), + * tmpnam(NULL), signal(). To make such C library routines thread-safe + * would require adding a ThreadLocalStorage struct to the thread control + * block TX_THREAD. Since relatively few applications make use of these + * library routines, the implementation provided here uses a single, global + * ThreadLocalStorage data structure rather than greatly increasing the size + * of the thread control block TX_THREAD. + * + * The ThreadX global variable _tx_thread_current_ptr points to the + * current thread's control block TX_THREAD. If a ThreadLocalStorage struct + * called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage + * should be modified to return &(_tx_thread_current_ptr->tx_tls). + */ + +static ThreadLocalStorage GlobalTLS; + +ThreadLocalStorage *GetThreadLocalStorage() +{ + return &GlobalTLS; +} +#endif + +/* + * Use a global ThreadX mutex to implement thread safety within C and C++ + * library routines. + * + */ +TX_MUTEX __ghLockMutex; + +/* + * Acquire general lock. Blocks until the lock becomes available. + * Use tx_mutex_get to implement __ghsLock + */ +void __ghsLock(void) +{ + tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER); +} + +/* + * Release general lock + * Use tx_mutex_put to implement __ghsUnlock + */ +void __ghsUnlock(void) +{ + tx_mutex_put(&__ghLockMutex); +} + +/* ThreadX Initialization function prototype. */ +void _tx_initialize_kernel_setup(void); + +void __gh_lock_init(void) +{ + /* Initialize the low-level portions of ThreadX. */ + _tx_initialize_kernel_setup(); + + /* Create the global thread lock mutex. */ + tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT); +} + +/* + Saving State Across setjmp() Calls + ================================== + + These routines can be used to save and restore arbitrary state + across calls to setjmp() and longjmp(). +*/ +int __ghs_SaveSignalContext(jmp_buf jmpbuf) +{ + return 0; +} + +/* Restore arbitrary state across a longjmp() */ +void __ghs_RestoreSignalContext(jmp_buf jmpbuf) +{ +} + +#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560) +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +#else + if (_tx_thread_current_ptr) + + /* Return thread-specific __eh_globals pointer. */ + return _tx_thread_current_ptr->tx_thread_eh_globals; + else + /* Return the global __eh_globals pointer. */ + return GlobalTLS.__eh_globals; +#endif +} +#endif + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500) +#pragma weak __cpp_exception_init +extern void __cpp_exception_init(void **); +#pragma weak __cpp_exception_cleanup +extern void __cpp_exception_cleanup(void **); + +/* __tx_cpp_exception_init retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_init. + */ +void __tx_cpp_exception_init(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_init) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_init(peh_globals); + } +} + +/* __tx_cpp_exception_cleanup retrieves the eh_globals field from + thread-local storage and calls __cpp_exception_cleanup. + */ +void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + void **peh_globals; + if(__cpp_exception_cleanup) { + if (thread_ptr) + peh_globals = &(thread_ptr->tx_thread_eh_globals); + else + /* Use the global __eh_globals pointer. */ + peh_globals = &__eh_globals; + __cpp_exception_cleanup(peh_globals); + } +} + +/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize + exceptions for the global context. + */ +void __ghs_cpp_exception_init() { + __tx_cpp_exception_init((void *)0); +} + +/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up + exceptions for the global context. + */ +void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) { + __tx_cpp_exception_cleanup((void *)0); +} +#endif + + +/* + File Locks + ====================== + + These routines can be customized to implement per-file locks to allow + thread-safe I/O. + +*/ + +/* Acquire lock for FILE *addr */ +void __ghs_flock_file(void *addr) +{ + tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER); +} + +/* Release lock for FILE *addr */ +void __ghs_funlock_file(void *addr) +{ + tx_mutex_put((TX_MUTEX *)addr); +} + +/* Non blocking acquire lock for FILE *addr. May return -1 if */ +/* not implemented. Returns 0 on success and nonzero otherwise. */ +int __ghs_ftrylock_file(void *addr) +{ + return -1; +} + +/* Calls to initialize local lock data structures before they */ +/* are used. */ +void __ghs_flock_create(void **addr) +{ + *addr = (void *)(&__ghLockMutex); +} +void __ghs_flock_destroy(void *addr) {} + + +/* + * ThreadX Peak Stack Checking support routines. + * + * All of these routines are called by MULTI's ThreadX-aware debugging + * package to determine the peak stack use for one thread or for all threads. + * + * These routines are included in this file in order to guarantee that they will + * be available while debugging with MULTI. These routines are not referenced by + * any other part of the ThreadX system. + * + * _txs_thread_stack_check: return the peak stack usage for a thread. + * + * _txs_thread_stack_check_2: store the peak stack usage for all threads + * in the tx_thread_stack_size field of each thread + * control block, TX_THREAD. This routine takes + * advantage of the redundancy within the TX_THREAD + * structure since tx_thread_stack_size can be computed + * from the tx_thread_stack_start and tx_thread_stack_end + * fields of TX_THREAD. + * + * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2 + * call by computing the stack size for each + * thread and storing the result in the + * tx_thread_stack_size field of each thread control + * block TX_THREAD. + * + * These three routines do not support architectures such as i960 or StarCore + * where the stack grows up instead of down. + * + */ +#ifndef TX_DISABLE_STACK_CHECKING + +ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr) +{ + CHAR *cp; /* Pointer inside thread's stack. */ + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start; + cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Return the number of bytes from cp up to and including the + end of the stack. */ + return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1); + } + } + return thread_ptr->tx_thread_stack_size; +} + + +int _txs_thread_stack_check_2(void) { + CHAR * cp; /* Pointer inside thread's stack. */ + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Search through the thread's stack to find the highest address modified. */ + for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end; + ++cp ) { + + /* Check if this byte in the stack contains something other than TX_STACK_FILL. */ + if (*cp != (char)TX_STACK_FILL) { + + /* Assume cp points to the locating marking the peak stack use. + Store the number of bytes from cp up to and including the + end of the stack in the tx_thread_stack_size field. */ + tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1; + break; + } + + } + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +int _txs_thread_stack_check_2_fixup(void) { + TX_THREAD * tp; /* Pointer to each thread. */ + + /* If no threads are created, return immediately. */ + if (!_tx_thread_created_count) + return 0; + + /* Start iterating through the threads in the system. Assume that we always + have at least one thread (the system timer thread) in the system. */ + tp = _tx_thread_created_ptr; + + do { + + /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and + tx_thread_stack_start fields. */ + tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1; + + /* Continue with the next thread. */ + tp = tp->tx_thread_created_next; + + /* Loop until we point to the first thread again. */ + } while ( tp != _tx_thread_created_ptr ); + + return 0; +} + +#endif /* TX_DISABLE_STACK_CHECKING */ diff --git a/ports/cortex_r7/ghs/src/tx_ghse.c b/ports/cortex_r7/ghs/src/tx_ghse.c new file mode 100644 index 00000000..6369df77 --- /dev/null +++ b/ports/cortex_r7/ghs/src/tx_ghse.c @@ -0,0 +1,49 @@ +/* + * ThreadX C++ Library Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ +#include "tx_ghs.h" +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* + C++ Exception Handling + ====================== + + These routines allow C++ exceptions to be used in multiple threads. + The default implementation uses __ghs_GetThreadLocalStorageItem + to return a thread-specific __eh_globals pointer. + +*/ + +#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 560) +#ifdef _WIN32 +/* Windows uses a different linker, so include a stub routine, never called, + to pull in __cpp_exception_init and __cpp_exception_cleanup */ +extern void __cpp_exception_init(void **); +extern void __cpp_exception_cleanup(void **); +void __tx_win32_pull_in_exceptions(void) { + __cpp_exception_init(0); + __cpp_exception_cleanup(0); +} +#else +#pragma ghs reference __cpp_exception_init +#pragma ghs reference __cpp_exception_cleanup +#endif + +/* Must be called after __cpp_exception_init() is called to allocate + * and initialize the per-thread exception handling structure */ +void *__get_eh_globals(void) +{ + return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals); +} +#endif diff --git a/ports/cortex_r7/green/src/tx_thread_context_restore.arm b/ports/cortex_r7/ghs/src/tx_thread_context_restore.arm similarity index 96% rename from ports/cortex_r7/green/src/tx_thread_context_restore.arm rename to ports/cortex_r7/ghs/src/tx_thread_context_restore.arm index 96c4c91b..c6fd01ed 100644 --- a/ports/cortex_r7/green/src/tx_thread_context_restore.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_restore Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the interrupt context if it is processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -114,13 +114,13 @@ _tx_thread_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -132,7 +132,7 @@ _tx_thread_context_restore: __tx_thread_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -221,7 +221,7 @@ _tx_skip_irq_vfp_save: /* _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; _tx_timer_time_slice = 0; */ - + STR r2, [r0, 24] # Save thread's time-slice MOV r2, 0 # Clear value STR r2, [r3] # Disable global time-slice flag diff --git a/ports/cortex_r7/green/src/tx_thread_context_save.arm b/ports/cortex_r7/ghs/src/tx_thread_context_save.arm similarity index 94% rename from ports/cortex_r7/green/src/tx_thread_context_save.arm rename to ports/cortex_r7/ghs/src/tx_thread_context_save.arm index fd00e91f..215c81a5 100644 --- a/ports/cortex_r7/green/src/tx_thread_context_save.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,40 +39,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_context_save Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -90,7 +90,7 @@ _tx_thread_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers #ifdef TX_ENABLE_FIQ_SUPPORT #ifdef TX_BEFORE_ARMV6 @@ -116,7 +116,7 @@ _tx_thread_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -132,7 +132,7 @@ _tx_thread_context_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing __tx_thread_not_nested_save: /* } */ @@ -146,13 +146,13 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, r10, r12, lr} # Store other registers /* Save the current stack pointer in the thread's control block. */ @@ -172,7 +172,7 @@ __tx_thread_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing /* } else @@ -182,7 +182,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit @@ -197,7 +197,7 @@ __tx_thread_idle_system_save: #endif ADD sp, sp, 16 # Recover saved registers - B __tx_irq_processing_return # Continue IRQ processing + B __tx_irq_processing_return # Continue IRQ processing .type _tx_thread_context_save,$function .size _tx_thread_context_save,.-_tx_thread_context_save diff --git a/ports/cortex_r7/green/src/tx_thread_fiq_context_restore.arm b/ports/cortex_r7/ghs/src/tx_thread_fiq_context_restore.arm similarity index 96% rename from ports/cortex_r7/green/src/tx_thread_fiq_context_restore.arm rename to ports/cortex_r7/ghs/src/tx_thread_fiq_context_restore.arm index 2a425acc..a745b0db 100644 --- a/ports/cortex_r7/green/src/tx_thread_fiq_context_restore.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_fiq_context_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -42,41 +42,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_restore Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_restore Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function restores the fiq interrupt context when processing a */ -/* nested interrupt. If not, it returns to the interrupt thread if no */ -/* preemption is necessary. Otherwise, if preemption is necessary or */ -/* if no thread was running, the function returns to the scheduler. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling routine */ -/* */ -/* CALLED BY */ -/* */ -/* FIQ ISR Interrupt Service Routines */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function restores the fiq interrupt context when processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* FIQ ISR Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -111,13 +111,13 @@ _tx_thread_fiq_context_restore: LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state SUB r2, r2, 1 # Decrement the counter - STR r2, [r3] # Store the counter + STR r2, [r3] # Store the counter CMP r2, 0 # Was this the first interrupt? BEQ __tx_thread_fiq_not_nested_restore # If so, not a nested restore /* Interrupts are nested. */ - /* Just recover the saved registers and return to the point of + /* Just recover the saved registers and return to the point of interrupt. */ LDMIA sp!, {r0, r10, r12, lr} # Recover SPSR, POI, and scratch regs @@ -129,7 +129,7 @@ _tx_thread_fiq_context_restore: __tx_thread_fiq_not_nested_restore: /* Determine if a thread was interrupted and no preemption is required. */ - /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + /* else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) || (_tx_thread_preempt_disable)) { */ @@ -188,7 +188,7 @@ __tx_thread_fiq_preempt_restore: MOV r5, SVC_MODE # Build SVC mode CPSR MSR CPSR_c, r5 # Enter SVC mode STMDB sp!, {r0-r3} # Save r0-r3 on thread's stack - + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer diff --git a/ports/cortex_r7/green/src/tx_thread_fiq_context_save.arm b/ports/cortex_r7/ghs/src/tx_thread_fiq_context_save.arm similarity index 93% rename from ports/cortex_r7/green/src/tx_thread_fiq_context_save.arm rename to ports/cortex_r7/ghs/src/tx_thread_fiq_context_save.arm index 4cee8b52..28984046 100644 --- a/ports/cortex_r7/green/src/tx_thread_fiq_context_save.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_fiq_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -33,40 +33,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_context_save Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_context_save Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -84,7 +84,7 @@ _tx_thread_fiq_context_save: /* if (_tx_thread_system_state++) { */ - STMDB sp!, {r0-r3} # Save some working registers + STMDB sp!, {r0-r3} # Save some working registers LDR r3, =_tx_thread_system_state # Pickup address of system state var LDR r2, [r3] # Pickup system state CMP r2, 0 # Is this the first interrupt? @@ -99,7 +99,7 @@ _tx_thread_fiq_context_save: calling ISR. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, r10, r12, lr} # Store other registers /* Return to the ISR. */ @@ -115,7 +115,7 @@ _tx_thread_fiq_context_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing __tx_thread_fiq_not_nested_save: /* } */ @@ -129,16 +129,16 @@ __tx_thread_fiq_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_fiq_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Save minimal context of interrupted thread. */ MRS r2, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r2, lr} # Store other registers, Note that we don't - /* # need to save sl and ip since FIQ has - # copies of these registers. Nested + /* # need to save sl and ip since FIQ has + # copies of these registers. Nested # interrupt processing does need to save # these registers. */ @@ -159,7 +159,7 @@ __tx_thread_fiq_not_nested_save: POP {lr} # Recover ISR lr #endif - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing /* } else @@ -179,15 +179,15 @@ __tx_thread_fiq_idle_system_save: #endif /* Not much to do here, save the current SPSR and LR for possible - use in IRQ interrupted in idle system conditions, and return to + use in IRQ interrupted in idle system conditions, and return to FIQ interrupt processing. */ MRS r0, SPSR # Pickup saved SPSR - SUB lr, lr, 4 # Adjust point of interrupt + SUB lr, lr, 4 # Adjust point of interrupt STMDB sp!, {r0, lr} # Store other registers that will get used - /* # or stripped off the stack in context + /* # or stripped off the stack in context # restore */ - B __tx_fiq_processing_return # Continue FIQ processing + B __tx_fiq_processing_return # Continue FIQ processing .type _tx_thread_fiq_context_save,$function .size _tx_thread_fiq_context_save,.-_tx_thread_fiq_context_save diff --git a/ports/cortex_r7/green/src/tx_thread_fiq_nesting_end.arm b/ports/cortex_r7/ghs/src/tx_thread_fiq_nesting_end.arm similarity index 91% rename from ports/cortex_r7/green/src/tx_thread_fiq_nesting_end.arm rename to ports/cortex_r7/ghs/src/tx_thread_fiq_nesting_end.arm index 3a300fd0..d6f4163b 100644 --- a/ports/cortex_r7/green/src/tx_thread_fiq_nesting_end.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_fiq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,48 +41,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_end Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_end Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ -/* processing from system mode back to FIQ mode prior to the ISR */ -/* calling _tx_thread_fiq_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_nesting_start has been called and switches the FIQ */ +/* processing from system mode back to FIQ mode prior to the ISR */ +/* calling _tx_thread_fiq_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_fiq_nesting_start.arm b/ports/cortex_r7/ghs/src/tx_thread_fiq_nesting_start.arm similarity index 92% rename from ports/cortex_r7/green/src/tx_thread_fiq_nesting_start.arm rename to ports/cortex_r7/ghs/src/tx_thread_fiq_nesting_start.arm index 95512d3b..d7483ca9 100644 --- a/ports/cortex_r7/green/src/tx_thread_fiq_nesting_start.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_fiq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,45 +39,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_fiq_nesting_start Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fiq_nesting_start Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from FIQ mode after */ -/* _tx_thread_fiq_context_save has been called and switches the FIQ */ -/* processing to the system mode so nested FIQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with FIQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from FIQ mode after */ +/* _tx_thread_fiq_context_save has been called and switches the FIQ */ +/* processing to the system mode so nested FIQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with FIQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_interrupt_control.arm b/ports/cortex_r7/ghs/src/tx_thread_interrupt_control.arm similarity index 92% rename from ports/cortex_r7/green/src/tx_thread_interrupt_control.arm rename to ports/cortex_r7/ghs/src/tx_thread_interrupt_control.arm index c881fd4d..71fac952 100644 --- a/ports/cortex_r7/green/src/tx_thread_interrupt_control.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_interrupt_control.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,39 +38,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_control Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for changing the interrupt lockout */ -/* posture of the system. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_interrupt_disable.arm b/ports/cortex_r7/ghs/src/tx_thread_interrupt_disable.arm similarity index 92% rename from ports/cortex_r7/green/src/tx_thread_interrupt_disable.arm rename to ports/cortex_r7/ghs/src/tx_thread_interrupt_disable.arm index 7507a10f..6e64ed2c 100644 --- a/ports/cortex_r7/green/src/tx_thread_interrupt_disable.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_interrupt_disable.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,38 +38,38 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_disable Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is responsible for disabling interrupts */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* old_posture Old interrupt lockout posture */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_interrupt_restore.arm b/ports/cortex_r7/ghs/src/tx_thread_interrupt_restore.arm similarity index 92% rename from ports/cortex_r7/green/src/tx_thread_interrupt_restore.arm rename to ports/cortex_r7/ghs/src/tx_thread_interrupt_restore.arm index af6e872e..ada7a6bf 100644 --- a/ports/cortex_r7/green/src/tx_thread_interrupt_restore.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_interrupt_restore.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -31,39 +31,39 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_interrupt_restore Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function is responsible for restoring interrupts to the state */ /* returned by a previous _tx_thread_interrupt_disable call. */ -/* */ -/* INPUT */ -/* */ -/* new_posture New interrupt lockout posture */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* Application Code */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_irq_nesting_end.arm b/ports/cortex_r7/ghs/src/tx_thread_irq_nesting_end.arm similarity index 91% rename from ports/cortex_r7/green/src/tx_thread_irq_nesting_end.arm rename to ports/cortex_r7/ghs/src/tx_thread_irq_nesting_end.arm index d628ef03..925dc046 100644 --- a/ports/cortex_r7/green/src/tx_thread_irq_nesting_end.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_irq_nesting_end.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -40,48 +40,48 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_end Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_end Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ -/* processing from system mode back to IRQ mode prior to the ISR */ -/* calling _tx_thread_context_restore. Note that this function */ -/* assumes the system stack pointer is in the same position after */ -/* nesting start function was called. */ -/* */ -/* This function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts disabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_irq_nesting_start has been called and switches the IRQ */ +/* processing from system mode back to IRQ mode prior to the ISR */ +/* calling _tx_thread_context_restore. Note that this function */ +/* assumes the system stack pointer is in the same position after */ +/* nesting start function was called. */ +/* */ +/* This function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts disabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_irq_nesting_start.arm b/ports/cortex_r7/ghs/src/tx_thread_irq_nesting_start.arm similarity index 92% rename from ports/cortex_r7/green/src/tx_thread_irq_nesting_start.arm rename to ports/cortex_r7/ghs/src/tx_thread_irq_nesting_start.arm index 8650b277..db1d3c64 100644 --- a/ports/cortex_r7/green/src/tx_thread_irq_nesting_start.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_irq_nesting_start.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,45 +38,45 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_irq_nesting_start Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_irq_nesting_start Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is called by the application from IRQ mode after */ -/* _tx_thread_context_save has been called and switches the IRQ */ -/* processing to the system mode so nested IRQ interrupt processing */ -/* is possible (system mode has its own "lr" register). Note that */ -/* this function assumes that the system mode stack pointer was setup */ -/* during low-level initialization (tx_initialize_low_level.arm). */ -/* */ -/* This function returns with IRQ interrupts enabled. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is called by the application from IRQ mode after */ +/* _tx_thread_context_save has been called and switches the IRQ */ +/* processing to the system mode so nested IRQ interrupt processing */ +/* is possible (system mode has its own "lr" register). Note that */ +/* this function assumes that the system mode stack pointer was setup */ +/* during low-level initialization (tx_initialize_low_level.arm). */ +/* */ +/* This function returns with IRQ interrupts enabled. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ diff --git a/ports/cortex_r7/green/src/tx_thread_schedule.arm b/ports/cortex_r7/ghs/src/tx_thread_schedule.arm similarity index 96% rename from ports/cortex_r7/green/src/tx_thread_schedule.arm rename to ports/cortex_r7/ghs/src/tx_thread_schedule.arm index 08f885b2..51638ac1 100644 --- a/ports/cortex_r7/green/src/tx_thread_schedule.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_schedule.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -38,42 +38,42 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_schedule Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function waits for a thread control block pointer to appear in */ -/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ -/* in the variable, the corresponding thread is resumed. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* _tx_thread_system_return Return to system from thread */ -/* _tx_thread_context_restore Restore thread's context */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -111,7 +111,7 @@ __tx_thread_schedule_loop: /* } while(_tx_thread_execute_ptr == TX_NULL); */ - + /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ @@ -134,7 +134,7 @@ __tx_thread_schedule_loop: MOV r0, v1 # Restore temp register #endif - LDR r1, =_tx_thread_current_ptr # Pickup address of current thread + LDR r1, =_tx_thread_current_ptr # Pickup address of current thread STR r0, [r1] # Setup current thread pointer /* Increment the run count for this thread. */ @@ -148,7 +148,7 @@ __tx_thread_schedule_loop: /* Setup time-slice, if present. */ /* _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; */ - LDR r2, =_tx_timer_time_slice # Pickup address of time slice + LDR r2, =_tx_timer_time_slice # Pickup address of time slice /* # variable */ LDR sp, [r0, 8] # Switch stack pointers STR r3, [r2] # Setup time-slice @@ -199,7 +199,7 @@ _tx_skip_solicited_vfp_restore: .type _tx_thread_schedule,$function .size _tx_thread_schedule,.-_tx_thread_schedule - + #ifdef __VFP__ .globl tx_thread_vfp_enable tx_thread_vfp_enable: diff --git a/ports/cortex_r7/green/src/tx_thread_stack_build.arm b/ports/cortex_r7/ghs/src/tx_thread_stack_build.arm similarity index 96% rename from ports/cortex_r7/green/src/tx_thread_stack_build.arm rename to ports/cortex_r7/ghs/src/tx_thread_stack_build.arm index d02abbe0..d34385d0 100644 --- a/ports/cortex_r7/green/src/tx_thread_stack_build.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_stack_build.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -41,41 +41,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_stack_build Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ +/* */ /* This function builds a stack frame on the supplied thread's stack. */ /* The stack frame results in a fake interrupt return to the supplied */ -/* function pointer. */ -/* */ -/* INPUT */ -/* */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ /* thread_ptr Pointer to thread control blk */ /* function_ptr Pointer to return function */ -/* */ -/* OUTPUT */ -/* */ +/* */ +/* OUTPUT */ +/* */ /* None */ -/* */ -/* CALLS */ -/* */ +/* */ +/* CALLS */ +/* */ /* None */ -/* */ -/* CALLED BY */ -/* */ +/* */ +/* CALLED BY */ +/* */ /* _tx_thread_create Create thread service */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -86,10 +86,10 @@ .globl _tx_thread_stack_build _tx_thread_stack_build: - + /* Build a fake interrupt frame. The form of the fake interrupt stack on the Cortex-R7 should look like the following after it is built: - + Stack Top: 1 Interrupt stack frame type CPSR Initial value for CPSR r0 (a1) Initial value for r0 diff --git a/ports/cortex_r7/green/src/tx_thread_system_return.arm b/ports/cortex_r7/ghs/src/tx_thread_system_return.arm similarity index 94% rename from ports/cortex_r7/green/src/tx_thread_system_return.arm rename to ports/cortex_r7/ghs/src/tx_thread_system_return.arm index 2ce606fa..183cd0c2 100644 --- a/ports/cortex_r7/green/src/tx_thread_system_return.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_system_return.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -37,41 +37,41 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_system_return Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function is target processor specific. It is used to transfer */ -/* control from a thread back to the ThreadX system. Only a */ -/* minimal context is saved since the compiler assumes temp registers */ -/* are going to get slicked by a function call anyway. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_thread_schedule Thread scheduling loop */ -/* */ -/* CALLED BY */ -/* */ -/* ThreadX components */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -90,7 +90,7 @@ _tx_thread_system_return: LDR r4, =_tx_thread_current_ptr # Pickup address of current ptr LDR r5, [r4] # Pickup current thread pointer - + #ifdef __VFP__ LDR r1, [r5, 144] # Pickup the VFP enabled flag CMP r1, 0 # Is the VFP enabled? @@ -104,7 +104,7 @@ _tx_skip_solicited_vfp_save: MOV r0, #0 # Build a solicited stack type MRS r1, CPSR # Pickup the CPSR STMDB sp!, {r0-r1} # Save type and CPSR - + /* Lockout interrupts. */ #ifdef TX_BEFORE_ARMV6 diff --git a/ports/cortex_r7/green/src/tx_thread_vectored_context_save.arm b/ports/cortex_r7/ghs/src/tx_thread_vectored_context_save.arm similarity index 95% rename from ports/cortex_r7/green/src/tx_thread_vectored_context_save.arm rename to ports/cortex_r7/ghs/src/tx_thread_vectored_context_save.arm index d6cae28a..2cece43a 100644 --- a/ports/cortex_r7/green/src/tx_thread_vectored_context_save.arm +++ b/ports/cortex_r7/ghs/src/tx_thread_vectored_context_save.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Thread */ /** */ @@ -39,40 +39,40 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_vectored_context_save Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_vectored_context_save Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function saves the context of an executing thread in the */ -/* beginning of interrupt processing. The function also ensures that */ -/* the system stack is used upon return to the calling ISR. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* ISRs */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -139,7 +139,7 @@ __tx_thread_not_nested_save: LDR r1, =_tx_thread_current_ptr # Pickup address of current thread ptr LDR r0, [r1] # Pickup current thread pointer CMP r0, 0 # Is it NULL? - BEQ __tx_thread_idle_system_save # If so, interrupt occurred in + BEQ __tx_thread_idle_system_save # If so, interrupt occurred in /* # scheduling loop - nothing needs saving! */ /* Note: Minimal context of interrupted thread is already saved. */ @@ -171,7 +171,7 @@ __tx_thread_idle_system_save: /* Interrupt occurred in the scheduling loop. */ - /* Not much to do here, just adjust the stack pointer, and return to IRQ + /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ MOV r10, 0 # Clear stack limit diff --git a/ports/cortex_r7/green/src/tx_timer_interrupt.arm b/ports/cortex_r7/ghs/src/tx_timer_interrupt.arm similarity index 95% rename from ports/cortex_r7/green/src/tx_timer_interrupt.arm rename to ports/cortex_r7/ghs/src/tx_timer_interrupt.arm index 0e785832..06ec1734 100644 --- a/ports/cortex_r7/green/src/tx_timer_interrupt.arm +++ b/ports/cortex_r7/ghs/src/tx_timer_interrupt.arm @@ -12,8 +12,8 @@ /**************************************************************************/ /**************************************************************************/ -/** */ -/** ThreadX Component */ +/** */ +/** ThreadX Component */ /** */ /** Timer */ /** */ @@ -32,43 +32,43 @@ .text .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_timer_interrupt Cortex-R7/Green Hills */ +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-R7/Green Hills */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ -/* */ -/* This function processes the hardware timer interrupt. This */ -/* processing includes incrementing the system clock and checking for */ -/* time slice and/or timer expiration. If either is found, the */ -/* interrupt context save/restore functions are called along with the */ -/* expiration functions. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* _tx_timer_expiration_process Process timer expiration */ -/* _tx_thread_time_slice Time slice interrupted thread */ -/* */ -/* CALLED BY */ -/* */ -/* interrupt vector */ -/* */ -/* RELEASE HISTORY */ -/* */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Process timer expiration */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ @@ -95,7 +95,7 @@ _tx_timer_interrupt: /* if (_tx_timer_time_slice) { */ - LDR r3, =_tx_timer_time_slice # Pickup address of time-slice + LDR r3, =_tx_timer_time_slice # Pickup address of time-slice LDR r2, [r3] # Pickup time-slice CMP r2, 0 # Is it non-active? BEQ __tx_timer_no_time_slice # Yes, skip time-slice processing @@ -212,7 +212,7 @@ __tx_timer_dont_activate: /* if (_tx_timer_expired_time_slice) { */ - LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired + LDR r3, =_tx_timer_expired_time_slice # Pickup addr of time-slice expired LDR r2, [r3] # Pickup the actual flag CMP r2, 0 # See if the flag is set BEQ __tx_timer_not_ts_expiration # No, skip time-slice processing diff --git a/ports/cortex_r7/ghs/src/txr_ghs.c b/ports/cortex_r7/ghs/src/txr_ghs.c new file mode 100644 index 00000000..19572e2b --- /dev/null +++ b/ports/cortex_r7/ghs/src/txr_ghs.c @@ -0,0 +1,84 @@ +/* + * ThreadX API Runtime Error Support + * + * Copyright 1983-2019 Green Hills Software LLC. + * + * This program is the property of Green Hills Software LLC., + * its contents are proprietary information and no part of it + * is to be disclosed to anyone except employees of Green Hills + * Software LLC., or as agreed in writing signed by the President + * of Green Hills Software LLC. + */ + +/* #include "tx_ghs.h" */ +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif +#include "tx_api.h" + +/* Customized ThreadX API runtime error support routine. */ + +void _rnerr(int num, int linenum, const char*str, void*ptr, ...); + +/* __ghs_rnerr() + This is the custom runtime error checking routine. + This implementation uses the existing __rnerr() routine. + Another implementation could use the .syscall mechanism, + provided MULTI was modified to understand that. + */ +void __ghs_rnerr(char *errMsg, int stackLevels, int stackTraceDisplay, void *hexVal) { + TX_INTERRUPT_SAVE_AREA + int num; + /* + Initialize the stack levels value. + + Add 3 to account for the calls to _rnerr, __rnerr, and + __ghs_rnerr. + + If the implementation changes, calls to __ghs_rnerr + will not need to be changed. + + Zero is not permitted, so substitute 3 in that case. + */ + num = (stackLevels+3) & 0xf; + if (!num) { + num = 3; + } + /* + Shift the stack levels value to bits 12..15 and + insert the stack trace display value in bit 11. + Bits 0..10 are unused. + */ + num = (num << 12) | (stackTraceDisplay ? 0x800 : 0); + + /* This will mask all interrupts in the RTEC code, which is probably + unacceptable for many targets. */ + TX_DISABLE + _rnerr(num, -1, (const char *)hexVal, (void *)errMsg); + TX_RESTORE +} + + +/* ThreadX thread stack checking runtime support routine. */ + +extern char __ghsbegin_stack[]; +extern TX_THREAD *_tx_thread_current_ptr; + +void __stkchk(void) { + int i; + if(_tx_thread_current_ptr) + { + if((unsigned)(&i) <= + (unsigned)(_tx_thread_current_ptr -> tx_thread_stack_start)) + { + _rnerr(21, -1, 0, 0); + } + } + else + { + if((unsigned)(&i) <= (unsigned)__ghsbegin_stack) + { + _rnerr(21, -1, 0, 0); + } + } +} diff --git a/ports/cortex_r7/green/example_build/sample_threadx.c b/ports/cortex_r7/green/example_build/sample_threadx.c deleted file mode 100644 index 418ec634..00000000 --- a/ports/cortex_r7/green/example_build/sample_threadx.c +++ /dev/null @@ -1,369 +0,0 @@ -/* 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; - - -/* 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", first_unused_memory, 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_r7/green/example_build/sample_threadx.ld b/ports/cortex_r7/green/example_build/sample_threadx.ld deleted file mode 100644 index 8d1ab4df..00000000 --- a/ports/cortex_r7/green/example_build/sample_threadx.ld +++ /dev/null @@ -1,44 +0,0 @@ -# The following explains what the default Green Hills sections are for: -# -# picbase - base of the text sections, relocatable in -pic mode -# text - text section -# syscall - syscall section, for host I/O under Multi -# fixaddr/fixtype - for PIC/PID fixups -# rodata - read only data -# romdata - the ROM image of .data -# romsdata - the ROM image of .sdata -# secinfo - section information section, used by the start-up code -# pidbase - base of the data sections, relocatable in -pid mode -# sdabase - base of the small data area section pointer -# sbss - small BSS (zeroed data) section -# sdata - small data section -# data - non-zeroed writeable data section -# bss - zeroed data section -# heap - the heap, grows upward -# stack - the stack, grows downward - --sec -{ - .reset 0x000000 : - .picbase 0x1000 : - .text : - .comment : - .intercall : - .interfunc : - .syscall : - .fixaddr : - .fixtype : - .rodata : - .romdata ROM(.data) : - .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : - .heap align(16) pad(0x10000) : - .stack align(16) pad(0x1000) : - .free_mem align(16) pad(0x10000) : -} diff --git a/ports/cortex_r7/green/example_build/sample_threadx_el.ld b/ports/cortex_r7/green/example_build/sample_threadx_el.ld deleted file mode 100644 index 33c0f934..00000000 --- a/ports/cortex_r7/green/example_build/sample_threadx_el.ld +++ /dev/null @@ -1,45 +0,0 @@ -# The following explains what the default Green Hills sections are for: -# -# picbase - base of the text sections, relocatable in -pic mode -# text - text section -# syscall - syscall section, for host I/O under Multi -# fixaddr/fixtype - for PIC/PID fixups -# rodata - read only data -# romdata - the ROM image of .data -# romsdata - the ROM image of .sdata -# secinfo - section information section, used by the start-up code -# pidbase - base of the data sections, relocatable in -pid mode -# sdabase - base of the small data area section pointer -# sbss - small BSS (zeroed data) section -# sdata - small data section -# data - non-zeroed writeable data section -# bss - zeroed data section -# heap - the heap, grows upward -# stack - the stack, grows downward - --sec -{ - .reset 0x000000 : - .picbase 0x1000 : - .text : - .comment : - .intercall : - .interfunc : - .syscall : - .fixaddr : - .fixtype : - .rodata : - .romdata ROM(.data) : - .romsdata ROM(.sdata) : - .secinfo : - .pidbase align(16) : - .sdabase : - .sbss : - .sdata : - .data : - .bss : - .heap align(16) pad(0x1000) : - .stack align(16) pad(0x1000) : - .eventlog align(16) pad(0x10000) : - .free_mem align(16) pad(0x10000) : -} diff --git a/ports/cortex_r7/green/example_build/tx.gpj b/ports/cortex_r7/green/example_build/tx.gpj deleted file mode 100644 index afbd6bef..00000000 --- a/ports/cortex_r7/green/example_build/tx.gpj +++ /dev/null @@ -1,283 +0,0 @@ -#!gbuild -[Library] - -I../../../../common/inc - -I../../../../ports_common_green/inc - -I../inc -..\..\..\..\common\inc\tx_api.h -..\..\..\..\common\inc\tx_block_pool.h -..\..\..\..\common\inc\tx_byte_pool.h -..\..\..\..\common\inc\tx_event_flags.h -..\..\..\..\common\inc\tx_initialize.h -..\..\..\..\common\inc\tx_mutex.h -..\..\..\..\common\inc\tx_queue.h -..\..\..\..\common\inc\tx_semaphore.h -..\..\..\..\common\inc\tx_thread.h -..\..\..\..\common\inc\tx_timer.h -..\..\..\..\common\inc\tx_trace.h -..\..\..\..\common\inc\tx_user_sample.h -..\inc\tx_port.h -..\..\..\..\ports_common_green\inc\tx_el.h -..\..\..\..\ports_common_green\inc\tx_ghs.h -..\src\tx_thread_context_restore.arm -..\src\tx_thread_context_save.arm -..\src\tx_thread_fiq_context_restore.arm -..\src\tx_thread_fiq_context_save.arm -..\src\tx_thread_fiq_nesting_end.arm -..\src\tx_thread_fiq_nesting_start.arm -..\src\tx_thread_interrupt_control.arm -..\src\tx_thread_interrupt_disable.arm -..\src\tx_thread_interrupt_restore.arm -..\src\tx_thread_irq_nesting_end.arm -..\src\tx_thread_irq_nesting_start.arm -..\src\tx_thread_schedule.arm -..\src\tx_thread_stack_build.arm -..\src\tx_thread_system_return.arm -..\src\tx_thread_vectored_context_save.arm -..\src\tx_timer_interrupt.arm -..\..\..\..\common\src\tx_block_allocate.c -..\..\..\..\common\src\tx_block_pool_cleanup.c -..\..\..\..\common\src\tx_block_pool_create.c -..\..\..\..\common\src\tx_block_pool_delete.c -..\..\..\..\common\src\tx_block_pool_info_get.c -..\..\..\..\common\src\tx_block_pool_initialize.c -..\..\..\..\common\src\tx_block_pool_performance_info_get.c -..\..\..\..\common\src\tx_block_pool_performance_system_info_get.c -..\..\..\..\common\src\tx_block_pool_prioritize.c -..\..\..\..\common\src\tx_block_release.c -..\..\..\..\common\src\tx_byte_allocate.c -..\..\..\..\common\src\tx_byte_pool_cleanup.c -..\..\..\..\common\src\tx_byte_pool_create.c -..\..\..\..\common\src\tx_byte_pool_delete.c -..\..\..\..\common\src\tx_byte_pool_info_get.c -..\..\..\..\common\src\tx_byte_pool_initialize.c -..\..\..\..\common\src\tx_byte_pool_performance_info_get.c -..\..\..\..\common\src\tx_byte_pool_performance_system_info_get.c -..\..\..\..\common\src\tx_byte_pool_prioritize.c -..\..\..\..\common\src\tx_byte_pool_search.c -..\..\..\..\common\src\tx_byte_release.c -..\..\..\..\common\src\tx_event_flags_cleanup.c -..\..\..\..\common\src\tx_event_flags_create.c -..\..\..\..\common\src\tx_event_flags_delete.c -..\..\..\..\common\src\tx_event_flags_get.c -..\..\..\..\common\src\tx_event_flags_info_get.c -..\..\..\..\common\src\tx_event_flags_initialize.c -..\..\..\..\common\src\tx_event_flags_performance_info_get.c -..\..\..\..\common\src\tx_event_flags_performance_system_info_get.c -..\..\..\..\common\src\tx_event_flags_set.c -..\..\..\..\common\src\tx_event_flags_set_notify.c -..\..\..\..\common\src\tx_initialize_high_level.c -..\..\..\..\common\src\tx_initialize_kernel_enter.c -..\..\..\..\common\src\tx_initialize_kernel_setup.c -..\..\..\..\common\src\tx_mutex_cleanup.c -..\..\..\..\common\src\tx_mutex_create.c -..\..\..\..\common\src\tx_mutex_delete.c -..\..\..\..\common\src\tx_mutex_get.c -..\..\..\..\common\src\tx_mutex_info_get.c -..\..\..\..\common\src\tx_mutex_initialize.c -..\..\..\..\common\src\tx_mutex_performance_info_get.c -..\..\..\..\common\src\tx_mutex_performance_system_info_get.c -..\..\..\..\common\src\tx_mutex_prioritize.c -..\..\..\..\common\src\tx_mutex_priority_change.c -..\..\..\..\common\src\tx_mutex_put.c -..\..\..\..\common\src\tx_queue_cleanup.c -..\..\..\..\common\src\tx_queue_create.c -..\..\..\..\common\src\tx_queue_delete.c -..\..\..\..\common\src\tx_queue_flush.c -..\..\..\..\common\src\tx_queue_front_send.c -..\..\..\..\common\src\tx_queue_info_get.c -..\..\..\..\common\src\tx_queue_initialize.c -..\..\..\..\common\src\tx_queue_performance_info_get.c -..\..\..\..\common\src\tx_queue_performance_system_info_get.c -..\..\..\..\common\src\tx_queue_prioritize.c -..\..\..\..\common\src\tx_queue_receive.c -..\..\..\..\common\src\tx_queue_send.c -..\..\..\..\common\src\tx_queue_send_notify.c -..\..\..\..\common\src\tx_semaphore_ceiling_put.c -..\..\..\..\common\src\tx_semaphore_cleanup.c -..\..\..\..\common\src\tx_semaphore_create.c -..\..\..\..\common\src\tx_semaphore_delete.c -..\..\..\..\common\src\tx_semaphore_get.c -..\..\..\..\common\src\tx_semaphore_info_get.c -..\..\..\..\common\src\tx_semaphore_initialize.c -..\..\..\..\common\src\tx_semaphore_performance_info_get.c -..\..\..\..\common\src\tx_semaphore_performance_system_info_get.c -..\..\..\..\common\src\tx_semaphore_prioritize.c -..\..\..\..\common\src\tx_semaphore_put.c -..\..\..\..\common\src\tx_semaphore_put_notify.c -..\..\..\..\common\src\tx_thread_create.c -..\..\..\..\common\src\tx_thread_delete.c -..\..\..\..\common\src\tx_thread_entry_exit_notify.c -..\..\..\..\common\src\tx_thread_identify.c -..\..\..\..\common\src\tx_thread_info_get.c -..\..\..\..\common\src\tx_thread_initialize.c -..\..\..\..\common\src\tx_thread_performance_info_get.c -..\..\..\..\common\src\tx_thread_performance_system_info_get.c -..\..\..\..\common\src\tx_thread_preemption_change.c -..\..\..\..\common\src\tx_thread_priority_change.c -..\..\..\..\common\src\tx_thread_relinquish.c -..\..\..\..\common\src\tx_thread_reset.c -..\..\..\..\common\src\tx_thread_resume.c -..\..\..\..\common\src\tx_thread_shell_entry.c -..\..\..\..\common\src\tx_thread_sleep.c -..\..\..\..\common\src\tx_thread_stack_analyze.c -..\..\..\..\common\src\tx_thread_stack_error_handler.c -..\..\..\..\common\src\tx_thread_stack_error_notify.c -..\..\..\..\common\src\tx_thread_suspend.c -..\..\..\..\common\src\tx_thread_system_preempt_check.c -..\..\..\..\common\src\tx_thread_system_resume.c -..\..\..\..\common\src\tx_thread_system_suspend.c -..\..\..\..\common\src\tx_thread_terminate.c -..\..\..\..\common\src\tx_thread_time_slice.c -..\..\..\..\common\src\tx_thread_time_slice_change.c -..\..\..\..\common\src\tx_thread_timeout.c -..\..\..\..\common\src\tx_thread_wait_abort.c -..\..\..\..\common\src\tx_time_get.c -..\..\..\..\common\src\tx_time_set.c -..\..\..\..\common\src\tx_timer_activate.c -..\..\..\..\common\src\tx_timer_change.c -..\..\..\..\common\src\tx_timer_create.c -..\..\..\..\common\src\tx_timer_deactivate.c -..\..\..\..\common\src\tx_timer_delete.c -..\..\..\..\common\src\tx_timer_expiration_process.c -..\..\..\..\common\src\tx_timer_info_get.c -..\..\..\..\common\src\tx_timer_initialize.c -..\..\..\..\common\src\tx_timer_performance_info_get.c -..\..\..\..\common\src\tx_timer_performance_system_info_get.c -..\..\..\..\common\src\tx_timer_system_activate.c -..\..\..\..\common\src\tx_timer_system_deactivate.c -..\..\..\..\common\src\tx_timer_thread_entry.c -..\..\..\..\common\src\tx_trace_buffer_full_notify.c -..\..\..\..\common\src\tx_trace_disable.c -..\..\..\..\common\src\tx_trace_enable.c -..\..\..\..\common\src\tx_trace_event_filter.c -..\..\..\..\common\src\tx_trace_event_unfilter.c -..\..\..\..\common\src\tx_trace_initialize.c -..\..\..\..\common\src\tx_trace_interrupt_control.c -..\..\..\..\common\src\tx_trace_isr_enter_insert.c -..\..\..\..\common\src\tx_trace_isr_exit_insert.c -..\..\..\..\common\src\tx_trace_object_register.c -..\..\..\..\common\src\tx_trace_object_unregister.c -..\..\..\..\common\src\tx_trace_user_event_insert.c -..\..\..\..\common\src\txe_block_allocate.c -..\..\..\..\common\src\txe_block_pool_create.c -..\..\..\..\common\src\txe_block_pool_delete.c -..\..\..\..\common\src\txe_block_pool_info_get.c -..\..\..\..\common\src\txe_block_pool_prioritize.c -..\..\..\..\common\src\txe_block_release.c -..\..\..\..\common\src\txe_byte_allocate.c -..\..\..\..\common\src\txe_byte_pool_create.c -..\..\..\..\common\src\txe_byte_pool_delete.c -..\..\..\..\common\src\txe_byte_pool_info_get.c -..\..\..\..\common\src\txe_byte_pool_prioritize.c -..\..\..\..\common\src\txe_byte_release.c -..\..\..\..\common\src\txe_event_flags_create.c -..\..\..\..\common\src\txe_event_flags_delete.c -..\..\..\..\common\src\txe_event_flags_get.c -..\..\..\..\common\src\txe_event_flags_info_get.c -..\..\..\..\common\src\txe_event_flags_set.c -..\..\..\..\common\src\txe_event_flags_set_notify.c -..\..\..\..\common\src\txe_mutex_create.c -..\..\..\..\common\src\txe_mutex_delete.c -..\..\..\..\common\src\txe_mutex_get.c -..\..\..\..\common\src\txe_mutex_info_get.c -..\..\..\..\common\src\txe_mutex_prioritize.c -..\..\..\..\common\src\txe_mutex_put.c -..\..\..\..\common\src\txe_queue_create.c -..\..\..\..\common\src\txe_queue_delete.c -..\..\..\..\common\src\txe_queue_flush.c -..\..\..\..\common\src\txe_queue_front_send.c -..\..\..\..\common\src\txe_queue_info_get.c -..\..\..\..\common\src\txe_queue_prioritize.c -..\..\..\..\common\src\txe_queue_receive.c -..\..\..\..\common\src\txe_queue_send.c -..\..\..\..\common\src\txe_queue_send_notify.c -..\..\..\..\common\src\txe_semaphore_ceiling_put.c -..\..\..\..\common\src\txe_semaphore_create.c -..\..\..\..\common\src\txe_semaphore_delete.c -..\..\..\..\common\src\txe_semaphore_get.c -..\..\..\..\common\src\txe_semaphore_info_get.c -..\..\..\..\common\src\txe_semaphore_prioritize.c -..\..\..\..\common\src\txe_semaphore_put.c -..\..\..\..\common\src\txe_semaphore_put_notify.c -..\..\..\..\common\src\txe_thread_create.c -..\..\..\..\common\src\txe_thread_delete.c -..\..\..\..\common\src\txe_thread_entry_exit_notify.c -..\..\..\..\common\src\txe_thread_info_get.c -..\..\..\..\common\src\txe_thread_preemption_change.c -..\..\..\..\common\src\txe_thread_priority_change.c -..\..\..\..\common\src\txe_thread_relinquish.c -..\..\..\..\common\src\txe_thread_reset.c -..\..\..\..\common\src\txe_thread_resume.c -..\..\..\..\common\src\txe_thread_suspend.c -..\..\..\..\common\src\txe_thread_terminate.c -..\..\..\..\common\src\txe_thread_time_slice_change.c -..\..\..\..\common\src\txe_thread_wait_abort.c -..\..\..\..\common\src\txe_timer_activate.c -..\..\..\..\common\src\txe_timer_change.c -..\..\..\..\common\src\txe_timer_create.c -..\..\..\..\common\src\txe_timer_deactivate.c -..\..\..\..\common\src\txe_timer_delete.c -..\..\..\..\common\src\txe_timer_info_get.c -..\..\..\..\ports_common_green\src\tx_el.c -..\..\..\..\ports_common_green\src\tx_ghs.c -..\..\..\..\ports_common_green\src\tx_ghse.c -..\..\..\..\ports_common_green\src\txr_block_allocate.c -..\..\..\..\ports_common_green\src\txr_block_pool_create.c -..\..\..\..\ports_common_green\src\txr_block_pool_delete.c -..\..\..\..\ports_common_green\src\txr_block_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_block_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_block_release.c -..\..\..\..\ports_common_green\src\txr_byte_allocate.c -..\..\..\..\ports_common_green\src\txr_byte_pool_create.c -..\..\..\..\ports_common_green\src\txr_byte_pool_delete.c -..\..\..\..\ports_common_green\src\txr_byte_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_byte_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_byte_release.c -..\..\..\..\ports_common_green\src\txr_event_flags_create.c -..\..\..\..\ports_common_green\src\txr_event_flags_delete.c -..\..\..\..\ports_common_green\src\txr_event_flags_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_info_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_set.c -..\..\..\..\ports_common_green\src\txr_event_flags_set_notify.c -..\..\..\..\ports_common_green\src\txr_ghs.c -..\..\..\..\ports_common_green\src\txr_mutex_create.c -..\..\..\..\ports_common_green\src\txr_mutex_delete.c -..\..\..\..\ports_common_green\src\txr_mutex_get.c -..\..\..\..\ports_common_green\src\txr_mutex_info_get.c -..\..\..\..\ports_common_green\src\txr_mutex_prioritize.c -..\..\..\..\ports_common_green\src\txr_mutex_put.c -..\..\..\..\ports_common_green\src\txr_queue_create.c -..\..\..\..\ports_common_green\src\txr_queue_delete.c -..\..\..\..\ports_common_green\src\txr_queue_flush.c -..\..\..\..\ports_common_green\src\txr_queue_front_send.c -..\..\..\..\ports_common_green\src\txr_queue_info_get.c -..\..\..\..\ports_common_green\src\txr_queue_prioritize.c -..\..\..\..\ports_common_green\src\txr_queue_receive.c -..\..\..\..\ports_common_green\src\txr_queue_send.c -..\..\..\..\ports_common_green\src\txr_queue_send_notify.c -..\..\..\..\ports_common_green\src\txr_semaphore_ceiling_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_create.c -..\..\..\..\ports_common_green\src\txr_semaphore_delete.c -..\..\..\..\ports_common_green\src\txr_semaphore_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_info_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_prioritize.c -..\..\..\..\ports_common_green\src\txr_semaphore_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_put_notify.c -..\..\..\..\ports_common_green\src\txr_thread_create.c -..\..\..\..\ports_common_green\src\txr_thread_delete.c -..\..\..\..\ports_common_green\src\txr_thread_entry_exit_notify.c -..\..\..\..\ports_common_green\src\txr_thread_info_get.c -..\..\..\..\ports_common_green\src\txr_thread_preemption_change.c -..\..\..\..\ports_common_green\src\txr_thread_priority_change.c -..\..\..\..\ports_common_green\src\txr_thread_reset.c -..\..\..\..\ports_common_green\src\txr_thread_resume.c -..\..\..\..\ports_common_green\src\txr_thread_suspend.c -..\..\..\..\ports_common_green\src\txr_thread_terminate.c -..\..\..\..\ports_common_green\src\txr_thread_time_slice_change.c -..\..\..\..\ports_common_green\src\txr_thread_wait_abort.c -..\..\..\..\ports_common_green\src\txr_timer_activate.c -..\..\..\..\ports_common_green\src\txr_timer_change.c -..\..\..\..\ports_common_green\src\txr_timer_create.c -..\..\..\..\ports_common_green\src\txr_timer_deactivate.c -..\..\..\..\ports_common_green\src\txr_timer_delete.c -..\..\..\..\ports_common_green\src\txr_timer_info_get.c diff --git a/ports/cortex_r7/green/example_build/tx_initialize_low_level.arm b/ports/cortex_r7/green/example_build/tx_initialize_low_level.arm deleted file mode 100644 index 777091fc..00000000 --- a/ports/cortex_r7/green/example_build/tx_initialize_low_level.arm +++ /dev/null @@ -1,319 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) Microsoft Corporation. All rights reserved. */ -/* */ -/* This software is licensed under the Microsoft Software License */ -/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -/* and in the root directory of this software. */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Initialize */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - -/* #define TX_SOURCE_CODE */ - - -/* Include necessary system files. */ - -/* #include "tx_api.h" - #include "tx_initialize.h" - #include "tx_thread.h" - #include "tx_timer.h" */ - - SVC_MODE = 0xD3 # Disable IRQ/FIQ SVC mode - IRQ_MODE = 0xD2 # Disable IRQ/FIQ IRQ mode - FIQ_MODE = 0xD1 # Disable IRQ/FIQ FIQ mode - SYS_MODE = 0xDF # Disable IRQ/FIQ SYS mode - MODE_MASK = 0x1F # Mode mask - FIQ_STACK_SIZE = 512 # FIQ stack size - IRQ_STACK_SIZE = 1024 # IRQ stack size - SYS_STACK_SIZE = 1024 # SYS stack size - - .text - .align 4 -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-R7/Green Hills */ -/* 6.1 */ -/* AUTHOR */ -/* */ -/* William E. Lamie, 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 */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* */ -/**************************************************************************/ -/* VOID _tx_initialize_low_level(VOID) -{ */ - .globl _tx_initialize_low_level -_tx_initialize_low_level: - - /****** NOTE ****** We must be in SVC MODE at this point. Some monitors - enter this routine in USER mode and require a software interrupt to - change into SVC mode. */ - - /* Save the system stack pointer. */ - /* _tx_thread_system_stack_ptr = (VOID_PTR) (sp); */ - - LDR r1,=_tx_thread_system_stack_ptr # Pickup address of system stack ptr - STR sp, [r1] # Save system stack - - /* Pickup the first available memory address. */ - - LDR r0,=__ghsbegin_free_mem # Pickup free memory address - - /* Setup initial stack pointers for IRQ and FIQ modes. */ - - MRS r12, CPSR # Pickup current CPSR - MOV r1, r0 # Get first available memory -#ifdef TX_ENABLE_IRQ_NESTING - /* Setup the system mode stack for nested interrupt support */ - LDR r2, =SYS_STACK_SIZE # Pickup stack size - BIC r3, r12, MODE_MASK # Clear mode bits - ORR r3, r3, SYS_MODE # Build SYS mode CPSR - MSR CPSR_c, r3 # Enter SYS mode - ADD r1, r1, r2 # Calculate start of SYS stack - SUB r1, r1, 1 # Backup one byte - BIC r1, r1, 7 # Insure 8-byte alignment - MOV sp, r1 # Setup SYS stack pointer -#endif - LDR r2, =FIQ_STACK_SIZE # Pickup stack size - BIC r3, r12, MODE_MASK # Clear mode bits - ORR r3, r3, FIQ_MODE # Build FIQ mode CPSR - MSR CPSR_c, r3 # Enter FIQ mode - ADD r1, r1, r2 # Calculate start of FIQ stack - SUB r1, r1, 1 # Backup one byte - BIC r1, r1, 7 # Insure 8-byte alignment - MOV sp, r1 # Setup FIQ stack pointer - MOV r10, 0 # Clear sl - MOV r11, 0 # Clear fp - LDR r2, =IRQ_STACK_SIZE # Pickup IRQ (system stack size) - BIC r3, r12, MODE_MASK # Clear mode bits - ORR r3, r3, IRQ_MODE # Build IRQ mode CPSR - MSR CPSR_c, r3 # Enter IRQ mode - ADD r1, r1, r2 # Calculate start of IRQ stack - SUB r1, r1, 1 # Backup one byte - BIC r1, r1, 7 # Insure 8-byte alignment - MOV sp, r1 # Setup IRQ stack pointer - MSR CPSR_c, r12 # Restore previous mode - ADD r0, r1, 4 # Adjust the new free memory - - - /* Save the first available memory address. */ - /* _tx_initialize_unused_memory = (VOID_PTR) __ghsbegin_free_mem + [SYS_STACK] + FIQ_STACK + IRQ_STACK; */ - - LDR r2,=_tx_initialize_unused_memory # Pickup unused memory ptr address - STR r0, [r2] # Save first free memory address - - - /* Setup Timer for periodic interrupts. To generate timer interrupts with - the Green Hills simulator, enter the following command in the target - window: timer 9999 irq */ - - /* Done, return to caller. */ - - RET # Return to caller - - .type _tx_initialize_low_level,$function - .size _tx_initialize_low_level,.-_tx_initialize_low_level -/* } */ - - -/* Define shells for each of the interrupt vectors. */ - - .globl __tx_undefined -__tx_undefined: - B __tx_undefined # Undefined handler - - .type __tx_undefined,$function - .size __tx_undefined,.-__tx_undefined - - .globl __tx_swi_interrupt -__tx_swi_interrupt: - B __tx_swi_interrupt # Software interrupt handler - - .type __tx_swi_interrupt,$function - .size __tx_swi_interrupt,.-__tx_swi_interrupt - - .globl __tx_prefetch_handler -__tx_prefetch_handler: - B __tx_prefetch_handler # Prefetch exception handler - - .type __tx_prefetch_handler,$function - .size __tx_prefetch_handler,.-__tx_prefetch_handler - - .globl __tx_abort_handler -__tx_abort_handler: - B __tx_abort_handler # Abort exception handler - - .type __tx_abort_handler,$function - .size __tx_abort_handler,.-__tx_abort_handler - - .globl __tx_reserved_handler -__tx_reserved_handler: - B __tx_reserved_handler # Reserved exception handler - - .type __tx_reserved_handler,$function - .size __tx_reserved_handler,.-__tx_reserved_handler - - .globl __tx_irq_handler - .globl __tx_irq_processing_return -__tx_irq_handler: - - /* Jump to context save to save system context. */ - B _tx_thread_context_save - - .type __tx_irq_handler,$function - .size __tx_irq_handler,.-__tx_irq_handler - -__tx_irq_processing_return: - - /* At this point execution is still in the IRQ mode. The CPSR, point of - interrupt, and all C scratch registers are available for use. */ - -#ifdef TX_ENABLE_EVENT_LOGGING - MOV r0, 0 # Build interrupt code - BL _tx_el_interrupt # Call interrupt event logging -#endif - - /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start - from IRQ mode with interrupts disabled. This routine switches to the - system mode and returns with IRQ interrupts enabled. - - NOTE: It is very important to ensure all IRQ interrupts are cleared - prior to enabling nested IRQ interrupts. */ -#ifdef TX_ENABLE_IRQ_NESTING - BL _tx_thread_irq_nesting_start -#endif - - - /* For debug purpose, execute the timer interrupt processing here. In - a real system, some kind of status indication would have to be checked - before the timer interrupt handler could be called. */ - BL _tx_timer_interrupt # Timer interrupt handler - - /* Application IRQ handlers can be called here! */ - - /* If interrupt nesting was started earlier, the end of interrupt nesting - service must be called before returning to _tx_thread_context_restore. - This routine returns in processing in IRQ mode with interrupts disabled. */ -#ifdef TX_ENABLE_IRQ_NESTING - BL _tx_thread_irq_nesting_end -#endif - -#ifdef TX_ENABLE_EVENT_LOGGING - MOV r0, 0 # Build interrupt code - BL _tx_el_interrupt_end # Call interrupt event logging -#endif - - /* Jump to context restore to restore system context. */ - B _tx_thread_context_restore - - .type __tx_irq_processing_return,$function - .size __tx_irq_processing_return,.-__tx_irq_processing_return - -#ifdef TX_ENABLE_FIQ_SUPPORT - - .globl __tx_fiq_handler - .globl __tx_fiq_processing_return -__tx_fiq_handler: - /* Jump to fiq context save to save system context. */ - B _tx_thread_fiq_context_save - - .type __tx_fiq_handler,$function - .size __tx_fiq_handler,.-__tx_fiq_handler - -__tx_fiq_processing_return: - - /* At this point execution is still in the FIQ mode. The CPSR, point of - interrupt, and all C scratch registers are available for use. */ - -#ifdef TX_ENABLE_EVENT_LOGGING - MOV r0, 1 # Build interrupt code - BL _tx_el_interrupt # Call interrupt event logging -#endif - - /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start - from FIQ mode with interrupts disabled. This routine switches to the - system mode and returns with FIQ interrupts enabled. - - NOTE: It is very important to ensure all FIQ interrupts are cleared - prior to enabling nested FIQ interrupts. */ -#ifdef TX_ENABLE_FIQ_NESTING - BL _tx_thread_fiq_nesting_start -#endif - - /* Application FIQ handlers can be called here! */ - - /* If interrupt nesting was started earlier, the end of interrupt nesting - service must be called before returning to _tx_thread_fiq_context_restore. */ -#ifdef TX_ENABLE_FIQ_NESTING - BL _tx_thread_fiq_nesting_end -#endif - -#ifdef TX_ENABLE_EVENT_LOGGING - MOV r0, 1 # Build interrupt code - BL _tx_el_interrupt_end # Call interrupt event logging -#endif - - /* Jump to fiq context restore to restore system context. */ - B _tx_thread_fiq_context_restore - - .type __tx_fiq_processing_return,$function - .size __tx_fiq_processing_return,.-__tx_fiq_processing_return - -#else - .globl __tx_fiq_handler -__tx_fiq_handler: - B __tx_fiq_handler # FIQ interrupt handler - - .type __tx_fiq_handler,$function - .size __tx_fiq_handler,.-__tx_fiq_handler -#endif - - /* Reference build options and version ID to ensure they come in. */ - -BUILD_OPTIONS: - .data.w _tx_build_options -VERSION_ID: - .data.w _tx_version_id diff --git a/ports/cortex_r7/green/example_build/txe.gpj b/ports/cortex_r7/green/example_build/txe.gpj deleted file mode 100644 index 662bcd16..00000000 --- a/ports/cortex_r7/green/example_build/txe.gpj +++ /dev/null @@ -1,284 +0,0 @@ -#!gbuild -[Library] - -DTX_ENABLE_EVENT_LOGGING - -I../../../../common/inc - -I../../../../ports_common_green/inc - -I../inc -..\..\..\..\common\inc\tx_api.h -..\..\..\..\common\inc\tx_block_pool.h -..\..\..\..\common\inc\tx_byte_pool.h -..\..\..\..\common\inc\tx_event_flags.h -..\..\..\..\common\inc\tx_initialize.h -..\..\..\..\common\inc\tx_mutex.h -..\..\..\..\common\inc\tx_queue.h -..\..\..\..\common\inc\tx_semaphore.h -..\..\..\..\common\inc\tx_thread.h -..\..\..\..\common\inc\tx_timer.h -..\..\..\..\common\inc\tx_trace.h -..\..\..\..\common\inc\tx_user_sample.h -..\inc\tx_port.h -..\..\..\..\ports_common_green\inc\tx_el.h -..\..\..\..\ports_common_green\inc\tx_ghs.h -..\src\tx_thread_context_restore.arm -..\src\tx_thread_context_save.arm -..\src\tx_thread_fiq_context_restore.arm -..\src\tx_thread_fiq_context_save.arm -..\src\tx_thread_fiq_nesting_end.arm -..\src\tx_thread_fiq_nesting_start.arm -..\src\tx_thread_interrupt_control.arm -..\src\tx_thread_interrupt_disable.arm -..\src\tx_thread_interrupt_restore.arm -..\src\tx_thread_irq_nesting_end.arm -..\src\tx_thread_irq_nesting_start.arm -..\src\tx_thread_schedule.arm -..\src\tx_thread_stack_build.arm -..\src\tx_thread_system_return.arm -..\src\tx_thread_vectored_context_save.arm -..\src\tx_timer_interrupt.arm -..\..\..\..\common\src\tx_block_allocate.c -..\..\..\..\common\src\tx_block_pool_cleanup.c -..\..\..\..\common\src\tx_block_pool_create.c -..\..\..\..\common\src\tx_block_pool_delete.c -..\..\..\..\common\src\tx_block_pool_info_get.c -..\..\..\..\common\src\tx_block_pool_initialize.c -..\..\..\..\common\src\tx_block_pool_performance_info_get.c -..\..\..\..\common\src\tx_block_pool_performance_system_info_get.c -..\..\..\..\common\src\tx_block_pool_prioritize.c -..\..\..\..\common\src\tx_block_release.c -..\..\..\..\common\src\tx_byte_allocate.c -..\..\..\..\common\src\tx_byte_pool_cleanup.c -..\..\..\..\common\src\tx_byte_pool_create.c -..\..\..\..\common\src\tx_byte_pool_delete.c -..\..\..\..\common\src\tx_byte_pool_info_get.c -..\..\..\..\common\src\tx_byte_pool_initialize.c -..\..\..\..\common\src\tx_byte_pool_performance_info_get.c -..\..\..\..\common\src\tx_byte_pool_performance_system_info_get.c -..\..\..\..\common\src\tx_byte_pool_prioritize.c -..\..\..\..\common\src\tx_byte_pool_search.c -..\..\..\..\common\src\tx_byte_release.c -..\..\..\..\common\src\tx_event_flags_cleanup.c -..\..\..\..\common\src\tx_event_flags_create.c -..\..\..\..\common\src\tx_event_flags_delete.c -..\..\..\..\common\src\tx_event_flags_get.c -..\..\..\..\common\src\tx_event_flags_info_get.c -..\..\..\..\common\src\tx_event_flags_initialize.c -..\..\..\..\common\src\tx_event_flags_performance_info_get.c -..\..\..\..\common\src\tx_event_flags_performance_system_info_get.c -..\..\..\..\common\src\tx_event_flags_set.c -..\..\..\..\common\src\tx_event_flags_set_notify.c -..\..\..\..\common\src\tx_initialize_high_level.c -..\..\..\..\common\src\tx_initialize_kernel_enter.c -..\..\..\..\common\src\tx_initialize_kernel_setup.c -..\..\..\..\common\src\tx_mutex_cleanup.c -..\..\..\..\common\src\tx_mutex_create.c -..\..\..\..\common\src\tx_mutex_delete.c -..\..\..\..\common\src\tx_mutex_get.c -..\..\..\..\common\src\tx_mutex_info_get.c -..\..\..\..\common\src\tx_mutex_initialize.c -..\..\..\..\common\src\tx_mutex_performance_info_get.c -..\..\..\..\common\src\tx_mutex_performance_system_info_get.c -..\..\..\..\common\src\tx_mutex_prioritize.c -..\..\..\..\common\src\tx_mutex_priority_change.c -..\..\..\..\common\src\tx_mutex_put.c -..\..\..\..\common\src\tx_queue_cleanup.c -..\..\..\..\common\src\tx_queue_create.c -..\..\..\..\common\src\tx_queue_delete.c -..\..\..\..\common\src\tx_queue_flush.c -..\..\..\..\common\src\tx_queue_front_send.c -..\..\..\..\common\src\tx_queue_info_get.c -..\..\..\..\common\src\tx_queue_initialize.c -..\..\..\..\common\src\tx_queue_performance_info_get.c -..\..\..\..\common\src\tx_queue_performance_system_info_get.c -..\..\..\..\common\src\tx_queue_prioritize.c -..\..\..\..\common\src\tx_queue_receive.c -..\..\..\..\common\src\tx_queue_send.c -..\..\..\..\common\src\tx_queue_send_notify.c -..\..\..\..\common\src\tx_semaphore_ceiling_put.c -..\..\..\..\common\src\tx_semaphore_cleanup.c -..\..\..\..\common\src\tx_semaphore_create.c -..\..\..\..\common\src\tx_semaphore_delete.c -..\..\..\..\common\src\tx_semaphore_get.c -..\..\..\..\common\src\tx_semaphore_info_get.c -..\..\..\..\common\src\tx_semaphore_initialize.c -..\..\..\..\common\src\tx_semaphore_performance_info_get.c -..\..\..\..\common\src\tx_semaphore_performance_system_info_get.c -..\..\..\..\common\src\tx_semaphore_prioritize.c -..\..\..\..\common\src\tx_semaphore_put.c -..\..\..\..\common\src\tx_semaphore_put_notify.c -..\..\..\..\common\src\tx_thread_create.c -..\..\..\..\common\src\tx_thread_delete.c -..\..\..\..\common\src\tx_thread_entry_exit_notify.c -..\..\..\..\common\src\tx_thread_identify.c -..\..\..\..\common\src\tx_thread_info_get.c -..\..\..\..\common\src\tx_thread_initialize.c -..\..\..\..\common\src\tx_thread_performance_info_get.c -..\..\..\..\common\src\tx_thread_performance_system_info_get.c -..\..\..\..\common\src\tx_thread_preemption_change.c -..\..\..\..\common\src\tx_thread_priority_change.c -..\..\..\..\common\src\tx_thread_relinquish.c -..\..\..\..\common\src\tx_thread_reset.c -..\..\..\..\common\src\tx_thread_resume.c -..\..\..\..\common\src\tx_thread_shell_entry.c -..\..\..\..\common\src\tx_thread_sleep.c -..\..\..\..\common\src\tx_thread_stack_analyze.c -..\..\..\..\common\src\tx_thread_stack_error_handler.c -..\..\..\..\common\src\tx_thread_stack_error_notify.c -..\..\..\..\common\src\tx_thread_suspend.c -..\..\..\..\common\src\tx_thread_system_preempt_check.c -..\..\..\..\common\src\tx_thread_system_resume.c -..\..\..\..\common\src\tx_thread_system_suspend.c -..\..\..\..\common\src\tx_thread_terminate.c -..\..\..\..\common\src\tx_thread_time_slice.c -..\..\..\..\common\src\tx_thread_time_slice_change.c -..\..\..\..\common\src\tx_thread_timeout.c -..\..\..\..\common\src\tx_thread_wait_abort.c -..\..\..\..\common\src\tx_time_get.c -..\..\..\..\common\src\tx_time_set.c -..\..\..\..\common\src\tx_timer_activate.c -..\..\..\..\common\src\tx_timer_change.c -..\..\..\..\common\src\tx_timer_create.c -..\..\..\..\common\src\tx_timer_deactivate.c -..\..\..\..\common\src\tx_timer_delete.c -..\..\..\..\common\src\tx_timer_expiration_process.c -..\..\..\..\common\src\tx_timer_info_get.c -..\..\..\..\common\src\tx_timer_initialize.c -..\..\..\..\common\src\tx_timer_performance_info_get.c -..\..\..\..\common\src\tx_timer_performance_system_info_get.c -..\..\..\..\common\src\tx_timer_system_activate.c -..\..\..\..\common\src\tx_timer_system_deactivate.c -..\..\..\..\common\src\tx_timer_thread_entry.c -..\..\..\..\common\src\tx_trace_buffer_full_notify.c -..\..\..\..\common\src\tx_trace_disable.c -..\..\..\..\common\src\tx_trace_enable.c -..\..\..\..\common\src\tx_trace_event_filter.c -..\..\..\..\common\src\tx_trace_event_unfilter.c -..\..\..\..\common\src\tx_trace_initialize.c -..\..\..\..\common\src\tx_trace_interrupt_control.c -..\..\..\..\common\src\tx_trace_isr_enter_insert.c -..\..\..\..\common\src\tx_trace_isr_exit_insert.c -..\..\..\..\common\src\tx_trace_object_register.c -..\..\..\..\common\src\tx_trace_object_unregister.c -..\..\..\..\common\src\tx_trace_user_event_insert.c -..\..\..\..\common\src\txe_block_allocate.c -..\..\..\..\common\src\txe_block_pool_create.c -..\..\..\..\common\src\txe_block_pool_delete.c -..\..\..\..\common\src\txe_block_pool_info_get.c -..\..\..\..\common\src\txe_block_pool_prioritize.c -..\..\..\..\common\src\txe_block_release.c -..\..\..\..\common\src\txe_byte_allocate.c -..\..\..\..\common\src\txe_byte_pool_create.c -..\..\..\..\common\src\txe_byte_pool_delete.c -..\..\..\..\common\src\txe_byte_pool_info_get.c -..\..\..\..\common\src\txe_byte_pool_prioritize.c -..\..\..\..\common\src\txe_byte_release.c -..\..\..\..\common\src\txe_event_flags_create.c -..\..\..\..\common\src\txe_event_flags_delete.c -..\..\..\..\common\src\txe_event_flags_get.c -..\..\..\..\common\src\txe_event_flags_info_get.c -..\..\..\..\common\src\txe_event_flags_set.c -..\..\..\..\common\src\txe_event_flags_set_notify.c -..\..\..\..\common\src\txe_mutex_create.c -..\..\..\..\common\src\txe_mutex_delete.c -..\..\..\..\common\src\txe_mutex_get.c -..\..\..\..\common\src\txe_mutex_info_get.c -..\..\..\..\common\src\txe_mutex_prioritize.c -..\..\..\..\common\src\txe_mutex_put.c -..\..\..\..\common\src\txe_queue_create.c -..\..\..\..\common\src\txe_queue_delete.c -..\..\..\..\common\src\txe_queue_flush.c -..\..\..\..\common\src\txe_queue_front_send.c -..\..\..\..\common\src\txe_queue_info_get.c -..\..\..\..\common\src\txe_queue_prioritize.c -..\..\..\..\common\src\txe_queue_receive.c -..\..\..\..\common\src\txe_queue_send.c -..\..\..\..\common\src\txe_queue_send_notify.c -..\..\..\..\common\src\txe_semaphore_ceiling_put.c -..\..\..\..\common\src\txe_semaphore_create.c -..\..\..\..\common\src\txe_semaphore_delete.c -..\..\..\..\common\src\txe_semaphore_get.c -..\..\..\..\common\src\txe_semaphore_info_get.c -..\..\..\..\common\src\txe_semaphore_prioritize.c -..\..\..\..\common\src\txe_semaphore_put.c -..\..\..\..\common\src\txe_semaphore_put_notify.c -..\..\..\..\common\src\txe_thread_create.c -..\..\..\..\common\src\txe_thread_delete.c -..\..\..\..\common\src\txe_thread_entry_exit_notify.c -..\..\..\..\common\src\txe_thread_info_get.c -..\..\..\..\common\src\txe_thread_preemption_change.c -..\..\..\..\common\src\txe_thread_priority_change.c -..\..\..\..\common\src\txe_thread_relinquish.c -..\..\..\..\common\src\txe_thread_reset.c -..\..\..\..\common\src\txe_thread_resume.c -..\..\..\..\common\src\txe_thread_suspend.c -..\..\..\..\common\src\txe_thread_terminate.c -..\..\..\..\common\src\txe_thread_time_slice_change.c -..\..\..\..\common\src\txe_thread_wait_abort.c -..\..\..\..\common\src\txe_timer_activate.c -..\..\..\..\common\src\txe_timer_change.c -..\..\..\..\common\src\txe_timer_create.c -..\..\..\..\common\src\txe_timer_deactivate.c -..\..\..\..\common\src\txe_timer_delete.c -..\..\..\..\common\src\txe_timer_info_get.c -..\..\..\..\ports_common_green\src\tx_el.c -..\..\..\..\ports_common_green\src\tx_ghs.c -..\..\..\..\ports_common_green\src\tx_ghse.c -..\..\..\..\ports_common_green\src\txr_block_allocate.c -..\..\..\..\ports_common_green\src\txr_block_pool_create.c -..\..\..\..\ports_common_green\src\txr_block_pool_delete.c -..\..\..\..\ports_common_green\src\txr_block_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_block_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_block_release.c -..\..\..\..\ports_common_green\src\txr_byte_allocate.c -..\..\..\..\ports_common_green\src\txr_byte_pool_create.c -..\..\..\..\ports_common_green\src\txr_byte_pool_delete.c -..\..\..\..\ports_common_green\src\txr_byte_pool_info_get.c -..\..\..\..\ports_common_green\src\txr_byte_pool_prioritize.c -..\..\..\..\ports_common_green\src\txr_byte_release.c -..\..\..\..\ports_common_green\src\txr_event_flags_create.c -..\..\..\..\ports_common_green\src\txr_event_flags_delete.c -..\..\..\..\ports_common_green\src\txr_event_flags_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_info_get.c -..\..\..\..\ports_common_green\src\txr_event_flags_set.c -..\..\..\..\ports_common_green\src\txr_event_flags_set_notify.c -..\..\..\..\ports_common_green\src\txr_ghs.c -..\..\..\..\ports_common_green\src\txr_mutex_create.c -..\..\..\..\ports_common_green\src\txr_mutex_delete.c -..\..\..\..\ports_common_green\src\txr_mutex_get.c -..\..\..\..\ports_common_green\src\txr_mutex_info_get.c -..\..\..\..\ports_common_green\src\txr_mutex_prioritize.c -..\..\..\..\ports_common_green\src\txr_mutex_put.c -..\..\..\..\ports_common_green\src\txr_queue_create.c -..\..\..\..\ports_common_green\src\txr_queue_delete.c -..\..\..\..\ports_common_green\src\txr_queue_flush.c -..\..\..\..\ports_common_green\src\txr_queue_front_send.c -..\..\..\..\ports_common_green\src\txr_queue_info_get.c -..\..\..\..\ports_common_green\src\txr_queue_prioritize.c -..\..\..\..\ports_common_green\src\txr_queue_receive.c -..\..\..\..\ports_common_green\src\txr_queue_send.c -..\..\..\..\ports_common_green\src\txr_queue_send_notify.c -..\..\..\..\ports_common_green\src\txr_semaphore_ceiling_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_create.c -..\..\..\..\ports_common_green\src\txr_semaphore_delete.c -..\..\..\..\ports_common_green\src\txr_semaphore_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_info_get.c -..\..\..\..\ports_common_green\src\txr_semaphore_prioritize.c -..\..\..\..\ports_common_green\src\txr_semaphore_put.c -..\..\..\..\ports_common_green\src\txr_semaphore_put_notify.c -..\..\..\..\ports_common_green\src\txr_thread_create.c -..\..\..\..\ports_common_green\src\txr_thread_delete.c -..\..\..\..\ports_common_green\src\txr_thread_entry_exit_notify.c -..\..\..\..\ports_common_green\src\txr_thread_info_get.c -..\..\..\..\ports_common_green\src\txr_thread_preemption_change.c -..\..\..\..\ports_common_green\src\txr_thread_priority_change.c -..\..\..\..\ports_common_green\src\txr_thread_reset.c -..\..\..\..\ports_common_green\src\txr_thread_resume.c -..\..\..\..\ports_common_green\src\txr_thread_suspend.c -..\..\..\..\ports_common_green\src\txr_thread_terminate.c -..\..\..\..\ports_common_green\src\txr_thread_time_slice_change.c -..\..\..\..\ports_common_green\src\txr_thread_wait_abort.c -..\..\..\..\ports_common_green\src\txr_timer_activate.c -..\..\..\..\ports_common_green\src\txr_timer_change.c -..\..\..\..\ports_common_green\src\txr_timer_create.c -..\..\..\..\ports_common_green\src\txr_timer_deactivate.c -..\..\..\..\ports_common_green\src\txr_timer_delete.c -..\..\..\..\ports_common_green\src\txr_timer_info_get.c diff --git a/ports_module/cortex_a35/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_a35/ac6/module_lib/src/txm_module_initialize.S index 86c3aa4b..6f3ca88e 100644 --- a/ports_module/cortex_a35/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_a35/ac6/module_lib/src/txm_module_initialize.S @@ -20,7 +20,9 @@ /**************************************************************************/ /**************************************************************************/ +#ifndef TXM_MODULE_HEAP_SIZE #define TXM_MODULE_HEAP_SIZE 512 +#endif .text diff --git a/ports_module/cortex_a35_smp/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_a35_smp/ac6/module_lib/src/txm_module_initialize.S index 86c3aa4b..6f3ca88e 100644 --- a/ports_module/cortex_a35_smp/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_a35_smp/ac6/module_lib/src/txm_module_initialize.S @@ -20,7 +20,9 @@ /**************************************************************************/ /**************************************************************************/ +#ifndef TXM_MODULE_HEAP_SIZE #define TXM_MODULE_HEAP_SIZE 512 +#endif .text diff --git a/ports_module/cortex_a7/ac5/example_build/build_all.bat b/ports_module/cortex_a7/ac5/example_build/build_all.bat deleted file mode 100644 index 84dcc19c..00000000 --- a/ports_module/cortex_a7/ac5/example_build/build_all.bat +++ /dev/null @@ -1,7 +0,0 @@ - -call build_threadx.bat -call build_threadx_module_library.bat -call build_threadx_module_sample.bat -call ..\..\..\..\common_modules\utilities\module_to_c_array.exe sample_threadx_module.axf module_code.c -powershell -Command "(gc module_code.c) -replace 'unsigned', '__align(4096) unsigned' | Out-File module_code.c" -call build_threadx_module_manager_sample.bat diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx/.cproject b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..22cf258d --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx/.project b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..725328bd --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/.project @@ -0,0 +1,27 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.arm.debug.ds.nature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx/exceptions.c b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/exceptions.c new file mode 100644 index 00000000..ac700115 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/exceptions.c @@ -0,0 +1,92 @@ +/* +** Copyright (c) 2006-2017 Arm Limited (or its affiliates). All rights reserved. +** Use, modification and redistribution of this file is subject to your possession of a +** valid End User License Agreement for the Arm Product of which these examples are part of +** and your compliance with all applicable terms and conditions of such licence agreement. +*/ + +/* This file contains the default exception handlers and vector table. +All exceptions are handled in Handler mode. Processor state is automatically +pushed onto the stack when an exception occurs, and popped from the stack at +the end of the handler */ + + +/* Exception Handlers */ +/* Marking as __attribute__((interrupt)) avoids them being accidentally called from elsewhere */ + +__attribute__((interrupt)) void NMIException(void) +{ while(1); } + +extern void HardFaultException(void); + +void MemManage_Handler(void); + +void BusFault_Handler(void); + +void UsageFault_Handler(void); + +void __tx_SVCallHandler(void); + +__attribute__((interrupt)) void DebugMonitor(void) +{ while(1); } + +void __tx_PendSVHandler(void); + +void __tx_SysTickHandler(void); + +__attribute__((interrupt)) void InterruptHandler(void) +{ while(1); } + + +/* typedef for the function pointers in the vector table */ +typedef void(* const ExecFuncPtr)(void) __attribute__((interrupt)); + +/* Linker-generated Stack Base address */ +#ifdef TWO_REGION +extern unsigned int Image$$ARM_LIB_STACK$$ZI$$Limit; /* for Two Region model */ +#else +extern unsigned int Image$$ARM_LIB_STACKHEAP$$ZI$$Limit; /* for (default) One Region model */ +#endif + +/* Entry point for C run-time initialization */ +extern int __main(void); + + +/* Vector table +Create a named ELF section for the vector table that can be placed in a scatter file. +The first two entries are: + Initial SP = |Image$$ARM_LIB_STACKHEAP$$ZI$$Limit| for (default) One Region model + or |Image$$ARM_LIB_STACK$$ZI$$Limit| for Two Region model + Initial PC= &__main (with LSB set to indicate Thumb) +*/ + +ExecFuncPtr vector_table[] __attribute__((section("vectors"))) = { + /* Configure Initial Stack Pointer using linker-generated symbol */ +#ifdef TWO_REGION + #pragma import(__use_two_region_memory) + (ExecFuncPtr)&Image$$ARM_LIB_STACK$$ZI$$Limit, +#else /* (default) One Region model */ + (ExecFuncPtr)&Image$$ARM_LIB_STACKHEAP$$ZI$$Limit, +#endif + (ExecFuncPtr)__main, /* Initial PC, set to entry point */ + NMIException, + HardFaultException, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + 0, 0, 0, 0, /* Reserved */ + __tx_SVCallHandler, + DebugMonitor, + 0, /* Reserved */ + __tx_PendSVHandler, + __tx_SysTickHandler, + + /* Add up to 240 interrupt handlers, starting here... */ + InterruptHandler, + InterruptHandler, /* Some dummy interrupt handlers */ + InterruptHandler + /* + : + */ +}; + diff --git a/ports/cortex_a9/green/example_build/sample_threadx.c b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.c similarity index 98% rename from ports/cortex_a9/green/example_build/sample_threadx.c rename to ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.c index 418ec634..597f373c 100644 --- a/ports/cortex_a9/green/example_build/sample_threadx.c +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.c @@ -26,6 +26,7 @@ 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... */ @@ -71,7 +72,7 @@ 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", first_unused_memory, DEMO_BYTE_POOL_SIZE); + 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. */ diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..3843da4d --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..f093ce05 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,44 @@ +;******************************************************* +; Copyright (c) 2006-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************* + +; Scatter-file for Cortex-M3 bare-metal example + +; This scatter-file places the vector table, application code, data, stacks and heap at suitable addresses in the memory map. + +; The vector table is placed first at the start of the image. +; Code starts after the last entry in the vector table. +; Data is placed at an address that must correspond to RAM. +; Stack and Heap are placed using ARM_LIB_STACKHEAP, to eliminate the need to set stack-base or heap-base in the debugger. +; System Control Space registers appear at their architecturally-defined addresses, based at 0xE000E000. + + +LOAD_REGION 0x00000000 +{ + VECTORS +0 0xC0 ; 16 exceptions + up to 32 interrupts, 4 bytes each entry == 0xC0 + { + exceptions.o (vectors, +FIRST) ; from exceptions.c + } + + ; Code is placed immediately (+0) after the previous root region + ; (so code region will also be a root region) + CODE +0 + { + * (+RO) ; All program code, including library code + } + + DATA +0 + { + * (+RW, +ZI) ; All RW and ZI data + } + + ; Heap grows upwards from start of this region and + ; Stack grows downwards from end of this region + ; The Main Stack Pointer is initialized on reset to the top addresses of this region + ARM_LIB_STACKHEAP +0 EMPTY 0x1000 + { + } +} diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx/tx_initialize_low_level.S b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/tx_initialize_low_level.S new file mode 100644 index 00000000..0ccb4b72 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx/tx_initialize_low_level.S @@ -0,0 +1,238 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_timer_interrupt + .global __main + .global __tx_NMIHandler // NMI + .global __tx_BadHandler // HardFault + .global __tx_SVCallHandler // SVCall + .global __tx_DBGHandler // Monitor + .global __tx_PendSVHandler // PendSV + .global __tx_SysTickHandler // SysTick + .global __tx_IntHandler // Int 0 + + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .thumb_func +_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, =Image$$ARM_LIB_STACKHEAP$$ZI$$Limit // Build first free address + ADDS r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + + LDR r0, =0xE000ED08 // Build address of NVIC registers + LDR r1, =vector_table // Pickup address of vector table + STR r1, [r0] // Set vector table address + + /* Set system stack pointer from vector value. */ + + LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer + LDR r1, =vector_table // Pickup address of vector table + LDR r1, [r1] // Pickup reset stack pointer + STR r1, [r0] // Save system stack pointer + + /* Enable the cycle count register. */ + + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + MOVS r2, #1 + ORRS r1, r1, r2 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ + + LDR r0, =0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + MOVS r2, #0x14 + STR r1, [r0, r2] // Setup SysTick Reload Value + MOVS r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + LDR r2, =0xD18 + STR r1, [r0, r2] // Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + LDR r2, =0xD1C + STR r1, [r0, r2] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + LDR r2, =0xD20 + STR r1, [r0, r2] // 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. */ + + .global __tx_BadHandler + .thumb_func +__tx_BadHandler: + B __tx_BadHandler + +/* added to catch the hardfault */ + + .global __tx_HardfaultHandler + .thumb_func +__tx_HardfaultHandler: + B __tx_HardfaultHandler + +/* Generic interrupt handler template */ + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + + /* Do interrupt handler work here */ + /* BL .... */ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + +/* System Tick timer interrupt handler */ + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + + +/* NMI, DBG handlers */ + .global __tx_NMIHandler + .thumb_func +__tx_NMIHandler: + B __tx_NMIHandler + + .global __tx_DBGHandler + .thumb_func +__tx_DBGHandler: + B __tx_DBGHandler + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_enter + .thumb_func +_tx_execution_isr_enter: + BX LR +#endif + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_exit + .thumb_func +_tx_execution_isr_exit: + BX LR +#endif diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.cproject b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.cproject new file mode 100644 index 00000000..3f9e781c --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.cproject @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.project b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.project new file mode 100644 index 00000000..17f6aba3 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/.project @@ -0,0 +1,27 @@ + + + sample_threadx_module + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.arm.debug.ds.nature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/sample_threadx_module.c b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/sample_threadx_module.c new file mode 100644 index 00000000..f2647144 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/sample_threadx_module.c @@ -0,0 +1,432 @@ +/* This is a small demo of the high-performance ThreadX kernel running as a module. It includes + examples of eight threads of different priorities, using a message queue, semaphore, mutex, + event flags group, byte pool, and block pool. */ + +/* Specify that this is a module! */ + +#define TXM_MODULE + + +/* Include the ThreadX module definitions. */ + +#include "txm_module.h" + + +/* Define constants. */ + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the external memory area. */ + +#define EXTERNAL_MEMORY_SIZE (64 * 1024) +#define EXTERNAL_MEMORY (0x80000) + + +/* Define the pool space in the bss section of the module. ULONG is used to + get the word alignment. */ + +ULONG demo_module_pool_space[DEMO_BYTE_POOL_SIZE / 4]; + + +/* 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; + + +/* 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; +ULONG semaphore_0_puts; +ULONG event_0_sets; +ULONG queue_0_sends; + +/* 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); + +void semaphore_0_notify(TX_SEMAPHORE *semaphore_ptr) +{ + + if (semaphore_ptr == semaphore_0) + semaphore_0_puts++; +} + + +void event_0_notify(TX_EVENT_FLAGS_GROUP *event_flag_group_ptr) +{ + + if (event_flag_group_ptr == event_flags_0) + event_0_sets++; +} + + +void queue_0_notify(TX_QUEUE *queue_ptr) +{ + + if (queue_ptr == queue_0) + queue_0_sends++; +} + + +/* Define the module start function. */ + +void demo_module_start(ULONG id) +{ + +CHAR *pointer; + + /* Allocate all the objects. In MPU mode, modules cannot allocate control blocks within + their own memory area so they cannot corrupt the resident portion of ThreadX by overwriting + the control block(s). */ + txm_module_object_allocate((void*)&thread_0, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_1, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_2, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_3, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_4, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_5, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_6, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_7, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&queue_0, sizeof(TX_QUEUE)); + txm_module_object_allocate((void*)&semaphore_0, sizeof(TX_SEMAPHORE)); + txm_module_object_allocate((void*)&mutex_0, sizeof(TX_MUTEX)); + txm_module_object_allocate((void*)&event_flags_0, sizeof(TX_EVENT_FLAGS_GROUP)); + txm_module_object_allocate((void*)&byte_pool_0, sizeof(TX_BYTE_POOL)); + txm_module_object_allocate((void*)&block_pool_0, sizeof(TX_BLOCK_POOL)); + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(byte_pool_0, "module byte pool 0", demo_module_pool_space, 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + tx_queue_send_notify(queue_0, queue_0_notify); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(semaphore_0, "module semaphore 0", 1); + + tx_semaphore_put_notify(semaphore_0, semaphore_0_notify); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(event_flags_0, "module event flags 0"); + + tx_event_flags_set_notify(event_flags_0, event_0_notify); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(mutex_0, "module 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, "module 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) + { + /* Test external memory sharing. */ + *(ULONG *)EXTERNAL_MEMORY = 0xABABABAB; + + /* 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_module/cortex_m0+/ac6/example_build/sample_threadx_module/txm_module_preamble.S b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/txm_module_preamble.S new file mode 100644 index 00000000..1fcc5d15 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module/txm_module_preamble.S @@ -0,0 +1,62 @@ + .text + .align 4 + .syntax unified + .section Init + + // Define public symbols + .global __txm_module_preamble + + // Define application-specific start/stop entry points for the module + .global demo_module_start + + // Define common external references + .global _txm_module_thread_shell_entry + .global _txm_module_callback_request_thread_entry + + .eabi_attribute Tag_ABI_PCS_RO_data, 1 + .eabi_attribute Tag_ABI_PCS_R9_use, 1 + .eabi_attribute Tag_ABI_PCS_RW_data, 2 + +__txm_module_preamble: + .dc.l 0x4D4F4455 // Module ID + .dc.l 0x6 // Module Major Version + .dc.l 0x1 // Module Minor Version + .dc.l 32 // Module Preamble Size in 32-bit words + .dc.l 0x12345678 // Module ID (application defined) + .dc.l 0x01000007 // Module Properties where: + // Bits 31-24: Compiler ID + // 0 -> IAR + // 1 -> ARM + // 2 -> GNU + // Bit 0: 0 -> Privileged mode execution + // 1 -> User mode execution + // Bit 1: 0 -> No MPU protection + // 1 -> MPU protection (must have user mode selected) + // Bit 2: 0 -> Disable shared/external memory access + // 1 -> Enable shared/external memory access + .dc.l _txm_module_thread_shell_entry - __txm_module_preamble // Module Shell Entry Point + .dc.l demo_module_start - __txm_module_preamble // Module Start Thread Entry Point + .dc.l 0 // Module Stop Thread Entry Point + .dc.l 1 // Module Start/Stop Thread Priority + .dc.l 1024 // Module Start/Stop Thread Stack Size + .dc.l _txm_module_callback_request_thread_entry - __txm_module_preamble // Module Callback Thread Entry + .dc.l 1 // Module Callback Thread Priority + .dc.l 1024 // Module Callback Thread Stack Size + .dc.l 0x10000 // Module Code Size + .dc.l 0x10000 // Module Data Size + .dc.l 0 // Reserved 0 + .dc.l 0 // Reserved 1 + .dc.l 0 // Reserved 2 + .dc.l 0 // Reserved 3 + .dc.l 0 // Reserved 4 + .dc.l 0 // Reserved 5 + .dc.l 0 // Reserved 6 + .dc.l 0 // Reserved 7 + .dc.l 0 // Reserved 8 + .dc.l 0 // Reserved 9 + .dc.l 0 // Reserved 10 + .dc.l 0 // Reserved 11 + .dc.l 0 // Reserved 12 + .dc.l 0 // Reserved 13 + .dc.l 0 // Reserved 14 + .dc.l 0 // Reserved 15 diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.cproject b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.cproject new file mode 100644 index 00000000..d89095b9 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.cproject @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.project b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.project new file mode 100644 index 00000000..f1ca14fa --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/.project @@ -0,0 +1,27 @@ + + + sample_threadx_module_manager + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.arm.debug.ds.nature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/exceptions.c b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/exceptions.c new file mode 100644 index 00000000..ac700115 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/exceptions.c @@ -0,0 +1,92 @@ +/* +** Copyright (c) 2006-2017 Arm Limited (or its affiliates). All rights reserved. +** Use, modification and redistribution of this file is subject to your possession of a +** valid End User License Agreement for the Arm Product of which these examples are part of +** and your compliance with all applicable terms and conditions of such licence agreement. +*/ + +/* This file contains the default exception handlers and vector table. +All exceptions are handled in Handler mode. Processor state is automatically +pushed onto the stack when an exception occurs, and popped from the stack at +the end of the handler */ + + +/* Exception Handlers */ +/* Marking as __attribute__((interrupt)) avoids them being accidentally called from elsewhere */ + +__attribute__((interrupt)) void NMIException(void) +{ while(1); } + +extern void HardFaultException(void); + +void MemManage_Handler(void); + +void BusFault_Handler(void); + +void UsageFault_Handler(void); + +void __tx_SVCallHandler(void); + +__attribute__((interrupt)) void DebugMonitor(void) +{ while(1); } + +void __tx_PendSVHandler(void); + +void __tx_SysTickHandler(void); + +__attribute__((interrupt)) void InterruptHandler(void) +{ while(1); } + + +/* typedef for the function pointers in the vector table */ +typedef void(* const ExecFuncPtr)(void) __attribute__((interrupt)); + +/* Linker-generated Stack Base address */ +#ifdef TWO_REGION +extern unsigned int Image$$ARM_LIB_STACK$$ZI$$Limit; /* for Two Region model */ +#else +extern unsigned int Image$$ARM_LIB_STACKHEAP$$ZI$$Limit; /* for (default) One Region model */ +#endif + +/* Entry point for C run-time initialization */ +extern int __main(void); + + +/* Vector table +Create a named ELF section for the vector table that can be placed in a scatter file. +The first two entries are: + Initial SP = |Image$$ARM_LIB_STACKHEAP$$ZI$$Limit| for (default) One Region model + or |Image$$ARM_LIB_STACK$$ZI$$Limit| for Two Region model + Initial PC= &__main (with LSB set to indicate Thumb) +*/ + +ExecFuncPtr vector_table[] __attribute__((section("vectors"))) = { + /* Configure Initial Stack Pointer using linker-generated symbol */ +#ifdef TWO_REGION + #pragma import(__use_two_region_memory) + (ExecFuncPtr)&Image$$ARM_LIB_STACK$$ZI$$Limit, +#else /* (default) One Region model */ + (ExecFuncPtr)&Image$$ARM_LIB_STACKHEAP$$ZI$$Limit, +#endif + (ExecFuncPtr)__main, /* Initial PC, set to entry point */ + NMIException, + HardFaultException, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + 0, 0, 0, 0, /* Reserved */ + __tx_SVCallHandler, + DebugMonitor, + 0, /* Reserved */ + __tx_PendSVHandler, + __tx_SysTickHandler, + + /* Add up to 240 interrupt handlers, starting here... */ + InterruptHandler, + InterruptHandler, /* Some dummy interrupt handlers */ + InterruptHandler + /* + : + */ +}; + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx.scat b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx.scat new file mode 100644 index 00000000..f093ce05 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx.scat @@ -0,0 +1,44 @@ +;******************************************************* +; Copyright (c) 2006-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your possession of a +; valid End User License Agreement for the Arm Product of which these examples are part of +; and your compliance with all applicable terms and conditions of such licence agreement. +;******************************************************* + +; Scatter-file for Cortex-M3 bare-metal example + +; This scatter-file places the vector table, application code, data, stacks and heap at suitable addresses in the memory map. + +; The vector table is placed first at the start of the image. +; Code starts after the last entry in the vector table. +; Data is placed at an address that must correspond to RAM. +; Stack and Heap are placed using ARM_LIB_STACKHEAP, to eliminate the need to set stack-base or heap-base in the debugger. +; System Control Space registers appear at their architecturally-defined addresses, based at 0xE000E000. + + +LOAD_REGION 0x00000000 +{ + VECTORS +0 0xC0 ; 16 exceptions + up to 32 interrupts, 4 bytes each entry == 0xC0 + { + exceptions.o (vectors, +FIRST) ; from exceptions.c + } + + ; Code is placed immediately (+0) after the previous root region + ; (so code region will also be a root region) + CODE +0 + { + * (+RO) ; All program code, including library code + } + + DATA +0 + { + * (+RW, +ZI) ; All RW and ZI data + } + + ; Heap grows upwards from start of this region and + ; Stack grows downwards from end of this region + ; The Main Stack Pointer is initialized on reset to the top addresses of this region + ARM_LIB_STACKHEAP +0 EMPTY 0x1000 + { + } +} diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c new file mode 100644 index 00000000..210f0be0 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c @@ -0,0 +1,126 @@ +/* Small demonstration of the ThreadX module manager. */ + +#include "tx_api.h" +#include "txm_module.h" + + +#define DEMO_STACK_SIZE 1024 + +/* Define the ThreadX object control blocks... */ + +TX_THREAD module_manager; +TXM_MODULE_INSTANCE my_module; + + +/* Define the object pool area. */ + +UCHAR object_memory[16384]; + + +/* Define the module data pool area. */ + +#define MODULE_DATA_SIZE (256 * 1024) +#define MODULE_DATA (0x40000) + + +/* The module code should be loaded here. */ + +#define MODULE_CODE (0x30000) + + +/* Define the external memory area. */ + +#define EXTERNAL_MEMORY_SIZE (64 * 1024) +#define EXTERNAL_MEMORY (0x80000) + + +/* Define the count of memory faults. */ + +ULONG memory_faults; + + +/* Define thread prototypes. */ + +void module_manager_entry(ULONG thread_input); + + +/* Define fault handler. */ + +VOID module_fault_handler(TX_THREAD *thread, TXM_MODULE_INSTANCE *module) +{ + + /* Just increment the fault counter. */ + memory_faults++; +} + +/* 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 = (CHAR*)first_unused_memory; + + + tx_thread_create(&module_manager, "Module Manager Thread", module_manager_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; +} + + + + +/* Define the test threads. */ + +void module_manager_entry(ULONG thread_input) +{ + + /* Initialize the module manager. */ + txm_module_manager_initialize((void *) MODULE_DATA, MODULE_DATA_SIZE); + + txm_module_manager_object_pool_create(object_memory, sizeof(object_memory)); + + /* Register a fault handler. */ + txm_module_manager_memory_fault_notify(module_fault_handler); + + /* Load the module that is already there, in this example it is placed there by the multiple image download. */ + txm_module_manager_in_place_load(&my_module, "my module", (void *) MODULE_CODE); + + /* Enable a read/write shared memory region. */ + txm_module_manager_external_memory_enable(&my_module, (void *) EXTERNAL_MEMORY, EXTERNAL_MEMORY_SIZE, TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE); + + /* Start the module. */ + txm_module_manager_start(&my_module); + + /* Sleep for a while.... */ + tx_thread_sleep(1000); + + /* Stop the module. */ + txm_module_manager_stop(&my_module); + + /* Unload the module. */ + txm_module_manager_unload(&my_module); + + /* Load the module that is already there. */ + txm_module_manager_in_place_load(&my_module, "my module", (void *) MODULE_CODE); + + /* Start the module again. */ + txm_module_manager_start(&my_module); + + /* Now just spin... */ + while(1) + { + + tx_thread_sleep(100); + } +} diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch new file mode 100644 index 00000000..d4fd4e3d --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/tx_initialize_low_level.S b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/tx_initialize_low_level.S new file mode 100644 index 00000000..51c400b9 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/sample_threadx_module_manager/tx_initialize_low_level.S @@ -0,0 +1,232 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_timer_interrupt + .global __main + .global __tx_NMIHandler // NMI + .global __tx_BadHandler // HardFault + .global __tx_SVCallHandler // SVCall + .global __tx_DBGHandler // Monitor + .global __tx_PendSVHandler // PendSV + .global __tx_SysTickHandler // SysTick + .global __tx_IntHandler // Int 0 + + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .thumb_func +_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, =Image$$ARM_LIB_STACKHEAP$$ZI$$Limit // Build first free address + ADDS r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + + LDR r0, =0xE000E000 // Build address of NVIC registers + LDR r1, =vector_table // Pickup address of vector table + LDR r2, =0xD08 + STR r1, [r0, r2] // Set vector table address + + /* Set system stack pointer from vector value. */ + + LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer + LDR r1, =vector_table // Pickup address of vector table + LDR r1, [r1] // Pickup reset stack pointer + STR r1, [r0] // Save system stack pointer + + /* Enable the cycle count register. */ + + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + MOVS r2, #1 + ORRS r1, r1, r2 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ + + LDR r0, =0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + MOVS r2, #0x14 + STR r1, [r0, r2] // Setup SysTick Reload Value + MOVS r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + LDR r2, =0xD18 + STR r1, [r0, r2] // Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + LDR r2, =0xD1C + STR r1, [r0, r2] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + LDR r2, =0xD20 + STR r1, [r0, r2] // 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. */ + + .global __tx_BadHandler + .thumb_func +__tx_BadHandler: + B __tx_BadHandler + +/* Generic interrupt handler template */ + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + + /* Do interrupt handler work here */ + /* BL .... */ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + +/* System Tick timer interrupt handler */ + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + + +/* NMI, DBG handlers */ + .global __tx_NMIHandler + .thumb_func +__tx_NMIHandler: + B __tx_NMIHandler + + .global __tx_DBGHandler + .thumb_func +__tx_DBGHandler: + B __tx_DBGHandler + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_enter + .thumb_func +_tx_execution_isr_enter: + BX LR +#endif + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_exit + .thumb_func +_tx_execution_isr_exit: + BX LR +#endif diff --git a/ports_module/cortex_m0+/ac6/example_build/tx/.cproject b/ports_module/cortex_m0+/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..0516ff74 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/tx/.cproject @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/tx/.project b/ports_module/cortex_m0+/ac6/example_build/tx/.project new file mode 100644 index 00000000..207f353b --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/tx/.project @@ -0,0 +1,63 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_generic_module_manager + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_manager/inc + + + inc_generic_modules + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_generic_module_manager + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_manager/src + + + src_port_module_manager + 2 + $%7BPARENT-2-PROJECT_LOC%7D/module_manager/src + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/txm/.cproject b/ports_module/cortex_m0+/ac6/example_build/txm/.cproject new file mode 100644 index 00000000..1cf6986c --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/txm/.cproject @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/ac6/example_build/txm/.project b/ports_module/cortex_m0+/ac6/example_build/txm/.project new file mode 100644 index 00000000..8b510516 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/example_build/txm/.project @@ -0,0 +1,58 @@ + + + txm + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_generic_module_manager + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_manager/inc + + + inc_generic_modules + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic_module_lib + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_lib/src + + + src_port_module_lib + 2 + $%7BPARENT-2-PROJECT_LOC%7D/module_lib/src + + + diff --git a/ports_module/cortex_m0+/ac6/inc/tx_port.h b/ports_module/cortex_m0+/ac6/inc/tx_port.h new file mode 100644 index 00000000..3ebd67b4 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/inc/tx_port.h @@ -0,0 +1,523 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX Cortex-M0+ port. */ + +#define TX_INT_DISABLE 1 /* Disable interrupts */ +#define TX_INT_ENABLE 0 /* Enable interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0xE0001004) +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (0) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#define TX_INLINE_INITIALIZATION + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION VOID *tx_event_flags_group_module_instance; \ + VOID (*tx_event_flags_group_set_module_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *group_ptr); + +#define TX_QUEUE_EXTENSION VOID *tx_queue_module_instance; \ + VOID (*tx_queue_send_module_notify)(struct TX_QUEUE_STRUCT *queue_ptr); + +#define TX_SEMAPHORE_EXTENSION VOID *tx_semaphore_module_instance; \ + VOID (*tx_semaphore_put_module_notify)(struct TX_SEMAPHORE_STRUCT *semaphore_ptr); + +#define TX_TIMER_EXTENSION VOID *tx_timer_module_instance; \ + VOID (*tx_timer_module_expiration_function)(ULONG id); + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) + +#ifdef TX_ENABLE_FPU_SUPPORT + + +#ifdef TX_MISRA_ENABLE + +ULONG _tx_misra_control_get(void); +void _tx_misra_control_set(ULONG value); +ULONG _tx_misra_fpccr_get(void); +void _tx_misra_vfp_touch(void); + +#else + +__attribute__( ( always_inline ) ) static inline ULONG __get_control(void) +{ + +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); +} + + +__attribute__( ( always_inline ) ) static inline void __set_control(ULONG control_value) +{ + + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); +} + + +#endif + + +/* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA + in order to ensure no lazy stacking will occur. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } + +#endif + + +/* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. + If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + the lazy FPU save, then restore the CONTROL.FPCA state. */ + +#ifndef TX_MISRA_ENABLE + + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + __asm__ volatile ("vmov.f32 s0, s0"); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } \ + } \ + } \ + } +#else + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } +#endif + +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +#endif + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define the get system state macro. */ + +#ifndef TX_THREAD_GET_SYSTEM_STATE +#ifndef TX_MISRA_ENABLE + +__attribute__( ( always_inline ) ) static inline unsigned int __get_ipsr_value(void) +{ + +unsigned int ipsr_value; + + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} + + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_ipsr_value()) +#else +ULONG _tx_misra_ipsr_get(VOID); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) +#endif +#endif + + +/* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value + indicates that _tx_thread_system_return should not be called. */ + +#ifndef TX_THREAD_SYSTEM_RETURN_CHECK +#define TX_THREAD_SYSTEM_RETURN_CHECK(c) (c) = ((ULONG) _tx_thread_preempt_disable); +#endif + + +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to + prevent early scheduling on Cortex-M parts. */ + +#define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; + + +/* This ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) { (b) = 0; \ + (m) = (m) & (-(m)); \ + while ((m) >>= 1) \ + { \ + (b)++; \ + } \ + } + + +#endif + + +#ifndef TX_DISABLE_INLINE + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned int primask_value; + + __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); + __asm__ volatile (" CPSID i" : : : "memory" ); + return(primask_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int primask_value) +{ + + __asm__ volatile (" MSR PRIMASK,%0": : "r" (primask_value): "memory" ); +} + +__attribute__( ( always_inline ) ) static inline unsigned int __get_primask_value(void) +{ + +unsigned int primask_value; + + __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); + return(primask_value); +} + +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + + __asm__ volatile (" CPSIE i": : : "memory" ); +} + + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +unsigned int interrupt_save; + + *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (__get_ipsr_value() == 0) + { + interrupt_save = __get_primask_value(); + __enable_interrupts(); + __restore_interrupts(interrupt_save); + } +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + + +/* Redefine _tx_thread_system_return for improved performance. */ + +#define _tx_thread_system_return _tx_thread_system_return_inline + + +#else + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); +#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); +#endif + + +/* Define FPU extension for the Cortex-M7. Each is assumed to be called in the context of the executing + thread. These are no longer needed, but are preserved for backward compatibility only. */ + +void tx_thread_fpu_enable(void); +void tx_thread_fpu_disable(void); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M0+/AC6 Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_module/cortex_m0+/ac6/inc/txm_module_port.h b/ports_module/cortex_m0+/ac6/inc/txm_module_port.h new file mode 100644 index 00000000..155880c7 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/inc/txm_module_port.h @@ -0,0 +1,381 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* txm_module_port.h Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the basic module constants, interface structures, */ +/* and function prototypes. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TXM_MODULE_PORT_H +#define TXM_MODULE_PORT_H + +/* Determine if the optional Modules user define file should be used. */ + +#ifdef TXM_MODULE_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in txm_module_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "txm_module_user.h" +#endif + +/* It is assumed that the base ThreadX tx_port.h file has been modified to add the + following extensions to the ThreadX thread control block (this code should replace + the corresponding macro define in tx_port.h): + +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; + +The following extensions must also be defined in tx_port.h: + +#define TX_EVENT_FLAGS_GROUP_EXTENSION VOID *tx_event_flags_group_module_instance; \ + VOID (*tx_event_flags_group_set_module_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *group_ptr); + +#define TX_QUEUE_EXTENSION VOID *tx_queue_module_instance; \ + VOID (*tx_queue_send_module_notify)(struct TX_QUEUE_STRUCT *queue_ptr); + +#define TX_SEMAPHORE_EXTENSION VOID *tx_semaphore_module_instance; \ + VOID (*tx_semaphore_put_module_notify)(struct TX_SEMAPHORE_STRUCT *semaphore_ptr); + +#define TX_TIMER_EXTENSION VOID *tx_timer_module_instance; \ + VOID (*tx_timer_module_expiration_function)(ULONG id); +*/ + +/* Define the kernel stack size for a module thread. */ +#ifndef TXM_MODULE_KERNEL_STACK_SIZE +#define TXM_MODULE_KERNEL_STACK_SIZE 768 +#endif + +/* For the following 3 access control settings, change TEX and C, B, S (bits 21 through 16 of MPU_RASR) + * to reflect your system memory attributes (cache, shareable, memory type). */ +/* Code region access control: privileged read-only, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_CODE_ACCESS_CONTROL +#define TXM_MODULE_MPU_CODE_ACCESS_CONTROL 0x06070000 +#endif +/* Data region access control: execute never, read/write, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_DATA_ACCESS_CONTROL +#define TXM_MODULE_MPU_DATA_ACCESS_CONTROL 0x13070000 +#endif +/* Shared region access control: execute never, read-only, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_SHARED_ACCESS_CONTROL +#define TXM_MODULE_MPU_SHARED_ACCESS_CONTROL 0x12070000 +#endif + +/* Define constants specific to the tools the module can be built with for this particular modules port. */ + +#define TXM_MODULE_IAR_COMPILER 0x00000000 +#define TXM_MODULE_RVDS_COMPILER 0x01000000 +#define TXM_MODULE_GNU_COMPILER 0x02000000 +#define TXM_MODULE_COMPILER_MASK 0xFF000000 +#define TXM_MODULE_OPTIONS_MASK 0x000000FF + + +/* Define the properties for this particular module port. */ + +#define TXM_MODULE_MEMORY_PROTECTION_ENABLED + +#ifdef TXM_MODULE_MEMORY_PROTECTION_ENABLED +#define TXM_MODULE_REQUIRE_ALLOCATED_OBJECT_MEMORY +#else +#define TXM_MODULE_REQUIRE_LOCAL_OBJECT_MEMORY +#endif + +#define TXM_MODULE_USER_MODE 0x00000001 +#define TXM_MODULE_MEMORY_PROTECTION 0x00000002 +#define TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS 0x00000004 + + +/* Define the supported options for this module. */ + +#define TXM_MODULE_MANAGER_SUPPORTED_OPTIONS (TXM_MODULE_USER_MODE | TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS) +#define TXM_MODULE_MANAGER_REQUIRED_OPTIONS 0 + + +/* Define offset adjustments according to the compiler used to build the module. */ + +#define TXM_MODULE_IAR_SHELL_ADJUST 24 +#define TXM_MODULE_IAR_START_ADJUST 28 +#define TXM_MODULE_IAR_STOP_ADJUST 32 +#define TXM_MODULE_IAR_CALLBACK_ADJUST 44 + +#define TXM_MODULE_RVDS_SHELL_ADJUST 0 +#define TXM_MODULE_RVDS_START_ADJUST 0 +#define TXM_MODULE_RVDS_STOP_ADJUST 0 +#define TXM_MODULE_RVDS_CALLBACK_ADJUST 0 + +#define TXM_MODULE_GNU_SHELL_ADJUST 24 +#define TXM_MODULE_GNU_START_ADJUST 28 +#define TXM_MODULE_GNU_STOP_ADJUST 32 +#define TXM_MODULE_GNU_CALLBACK_ADJUST 44 + + +/* Define other module port-specific constants. */ + +/* Define INLINE_DECLARE to inline for ARM compiler. */ + +#define INLINE_DECLARE inline + +#ifdef TXM_MODULE_MANAGER_16_MPU + +/* Define the number of MPU entries assigned to the code and data sections. + On some Cortex-M7 parts, there are 16 total entries. ThreadX uses one for access + to the kernel entry function, thus 15 remain for code and data protection. */ +#define TXM_MODULE_MPU_TOTAL_ENTRIES 16 +#define TXM_MODULE_MPU_CODE_ENTRIES 4 +#define TXM_MODULE_MPU_DATA_ENTRIES 4 +#define TXM_MODULE_MPU_SHARED_ENTRIES 3 + +#define TXM_MODULE_MPU_KERNEL_ENTRY_INDEX 0 +#define TXM_MODULE_MPU_SHARED_INDEX 9 + +#define TXM_ENABLE_REGION 0x01 + +/* There are 2 registers to set up each MPU region: MPU_RBAR, MPU_RASR. */ +typedef struct TXM_MODULE_MPU_INFO_STRUCT +{ + ULONG txm_module_mpu_region_address; + ULONG txm_module_mpu_region_attribute_size; +} TXM_MODULE_MPU_INFO; +/* Shared memory region attributes. */ +#define TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE 1 +#define TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT 0x01000000 + +/* Define the port-extensions to the module manager instance structure. */ + +#define TXM_MODULE_MANAGER_PORT_EXTENSION \ + TXM_MODULE_MPU_INFO txm_module_instance_mpu_registers[TXM_MODULE_MPU_TOTAL_ENTRIES]; \ + ULONG txm_module_instance_shared_memory_count; \ + ULONG txm_module_instance_shared_memory_address[TXM_MODULE_MPU_SHARED_ENTRIES]; \ + ULONG txm_module_instance_shared_memory_length[TXM_MODULE_MPU_SHARED_ENTRIES]; + +#else /* TXM_MODULE_MANAGER_16_MPU is not defined */ + +/* Define the number of MPU entries assigned to the code and data sections. + On Cortex-M0/M0+, M4, and some M7 parts, there are 8 total entries. ThreadX uses one for access + to the kernel entry function, thus 7 remain for code and data protection. */ +#define TXM_MODULE_MANAGER_CODE_MPU_ENTRIES 4 +#define TXM_MODULE_MANAGER_DATA_MPU_ENTRIES 3 +#define TXM_MODULE_MANAGER_SHARED_MPU_INDEX 8 +#define TXM_MODULE_MANAGER_SHARED_MPU_REGION 4 + +/* Shared memory region attributes. */ +#define TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE 1 +#define TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT 0x01000000 + +/* Define the port-extensions to the module manager instance structure. */ + +#define TXM_MODULE_MANAGER_PORT_EXTENSION \ + ULONG txm_module_instance_mpu_registers[16]; \ + ULONG txm_module_instance_shared_memory_address; \ + ULONG txm_module_instance_shared_memory_length; + +#endif /* TXM_MODULE_MANAGER_16_MPU */ + +/* Define the memory fault information structure that is populated when a memory fault occurs. */ + + +typedef struct TXM_MODULE_MANAGER_MEMORY_FAULT_INFO_STRUCT +{ + TX_THREAD *txm_module_manager_memory_fault_info_thread_ptr; + VOID *txm_module_manager_memory_fault_info_code_location; + ULONG txm_module_manager_memory_fault_info_shcsr; + ULONG txm_module_manager_memory_fault_info_cfsr; + ULONG txm_module_manager_memory_fault_info_mmfar; + ULONG txm_module_manager_memory_fault_info_bfar; + ULONG txm_module_manager_memory_fault_info_control; + ULONG txm_module_manager_memory_fault_info_sp; + ULONG txm_module_manager_memory_fault_info_r0; + ULONG txm_module_manager_memory_fault_info_r1; + ULONG txm_module_manager_memory_fault_info_r2; + ULONG txm_module_manager_memory_fault_info_r3; + ULONG txm_module_manager_memory_fault_info_r4; + ULONG txm_module_manager_memory_fault_info_r5; + ULONG txm_module_manager_memory_fault_info_r6; + ULONG txm_module_manager_memory_fault_info_r7; + ULONG txm_module_manager_memory_fault_info_r8; + ULONG txm_module_manager_memory_fault_info_r9; + ULONG txm_module_manager_memory_fault_info_r10; + ULONG txm_module_manager_memory_fault_info_r11; + ULONG txm_module_manager_memory_fault_info_r12; + ULONG txm_module_manager_memory_fault_info_lr; + ULONG txm_module_manager_memory_fault_info_xpsr; +} TXM_MODULE_MANAGER_MEMORY_FAULT_INFO; + + +#define TXM_MODULE_MANAGER_FAULT_INFO \ + TXM_MODULE_MANAGER_MEMORY_FAULT_INFO _txm_module_manager_memory_fault_info; + +/* Define the macro to check the code alignment. */ + +#define TXM_MODULE_MANAGER_CHECK_CODE_ALIGNMENT(module_location, code_alignment) \ + { \ + ULONG temp; \ + temp = (ULONG) module_location; \ + temp = temp & (code_alignment - 1); \ + if (temp) \ + { \ + _tx_mutex_put(&_txm_module_manager_mutex); \ + return(TXM_MODULE_ALIGNMENT_ERROR); \ + } \ + } + + +/* Define the macro to adjust the alignment and size for code/data areas. */ + +#define TXM_MODULE_MANAGER_ALIGNMENT_ADJUST(module_preamble, code_size, code_alignment, data_size, data_alignment) _txm_module_manager_alignment_adjust(module_preamble, &code_size, &code_alignment, &data_size, &data_alignment); + + +/* Define the macro to adjust the symbols in the module preamble. */ + +#define TXM_MODULE_MANAGER_CALCULATE_ADJUSTMENTS(properties, shell_function_adjust, start_function_adjust, stop_function_adjust, callback_function_adjust) \ + if ((properties & TXM_MODULE_COMPILER_MASK) == TXM_MODULE_IAR_COMPILER) \ + { \ + shell_function_adjust = TXM_MODULE_IAR_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_IAR_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_IAR_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_IAR_CALLBACK_ADJUST; \ + } \ + else if ((properties & TXM_MODULE_COMPILER_MASK) == TXM_MODULE_RVDS_COMPILER) \ + { \ + shell_function_adjust = TXM_MODULE_RVDS_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_RVDS_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_RVDS_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_RVDS_CALLBACK_ADJUST; \ + } \ + else \ + { \ + shell_function_adjust = TXM_MODULE_GNU_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_GNU_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_GNU_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_GNU_CALLBACK_ADJUST; \ + } + + +/* Define the macro to populate the thread control block with module port-specific information. + Check if the module is in user mode and set up txm_module_thread_entry_info_kernel_call_dispatcher accordingly. +*/ + +#define TXM_MODULE_MANAGER_THREAD_SETUP(thread_ptr, module_instance) \ + thread_ptr -> tx_thread_module_current_user_mode = module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE; \ + thread_ptr -> tx_thread_module_user_mode = module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE; \ + if (thread_ptr -> tx_thread_module_user_mode) \ + { \ + thread_entry_info -> txm_module_thread_entry_info_kernel_call_dispatcher = _txm_module_manager_user_mode_entry; \ + } \ + else \ + { \ + thread_entry_info -> txm_module_thread_entry_info_kernel_call_dispatcher = _txm_module_manager_kernel_dispatch; \ + } + + +/* Define the macro to populate the module control block with module port-specific information. + If memory protection is enabled, set up the MPU registers. +*/ +#define TXM_MODULE_MANAGER_MODULE_SETUP(module_instance) \ + if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE) \ + { \ + if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) \ + { \ + _txm_module_manager_mm_register_setup(module_instance); \ + } \ + } \ + else \ + { \ + /* Do nothing. */ \ + } + +/* Define the macro to perform port-specific functions when unloading the module. */ +/* Nothing needs to be done for this port. */ +#define TXM_MODULE_MANAGER_MODULE_UNLOAD(module_instance) + + +/* Define the macros to perform port-specific checks when passing pointers to the kernel. */ + +/* Define macro to make sure object is inside the module's data. */ +#ifdef TXM_MODULE_MANAGER_16_MPU +#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \ + _txm_module_manager_inside_data_check(module_instance, obj_ptr, obj_size) +#else +#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \ + /* Check for overflow. */ \ + (((obj_ptr) < ((obj_ptr) + (obj_size))) && \ + /* Check if it's inside module data. */ \ + ((((obj_ptr) >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && \ + (((obj_ptr) + (obj_size)) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1))) || \ + /* Check if it's inside shared memory. */ \ + (((obj_ptr) >= (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address) && \ + (((obj_ptr) + (obj_size)) <= (ALIGN_TYPE) (module_instance -> txm_module_instance_shared_memory_address + module_instance -> txm_module_instance_shared_memory_length))))) +#endif + +/* Define some internal prototypes to this module port. */ + +#ifndef TX_SOURCE_CODE +#define txm_module_manager_memory_fault_notify _txm_module_manager_memory_fault_notify +#endif + + +#define TXM_MODULE_MANAGER_ADDITIONAL_PROTOTYPES \ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size, ULONG *code_alignment, ULONG *data_size, ULONG *data_alignment); \ +VOID _txm_module_manager_memory_fault_handler(VOID); \ +UINT _txm_module_manager_memory_fault_notify(VOID (*notify_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)); \ +VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance); \ +ULONG _txm_power_of_two_block_size(ULONG size); \ +ULONG _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length); \ +ULONG _txm_module_manager_region_size_get(ULONG block_size); \ +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size); + +#define TXM_MODULE_MANAGER_VERSION_ID \ +CHAR _txm_module_manager_version_id[] = \ + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M0+/AC6 Version 6.1.10 *"; + +#endif diff --git a/ports_module/cortex_m0+/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_m0+/ac6/module_lib/src/txm_module_initialize.S new file mode 100644 index 00000000..f93cbf48 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_lib/src/txm_module_initialize.S @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .global __use_two_region_memory + .global __scatterload + + .eabi_attribute Tag_ABI_PCS_RO_data, 1 + .eabi_attribute Tag_ABI_PCS_R9_use, 1 + .eabi_attribute Tag_ABI_PCS_RW_data, 2 + + .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_initialize Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the module c runtime. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* __scatterload Initialize C runtime */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_thread_shell_entry Start module thread */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _txm_module_initialize(VOID) + .global _txm_module_initialize + .thumb_func +_txm_module_initialize: + MOV r0, r8 // Copy r8-r11 + MOV r1, r9 + MOV r2, r10 + MOV r3, r11 + PUSH {r0-r7} // Save r4-r11 + MOV r0, r12 + MOV r1, lr + PUSH {r0-r1} // Save r12, lr + + //B __scatterload // Call ARM func to initialize variables + +// Override the __rt_exit function. + .global __rt_exit + .thumb_func +__rt_exit: + POP {r0-r1} // Restore r4-r12 and LR + MOV r12, r0 + MOV lr, r1 + POP {r0-r7} + MOV r8, r0 + MOV r9, r1 + MOV r10, r2 + MOV r11, r3 + BX lr // Return to caller + +#ifndef TXM_MODULE_HEAP_SIZE +#define TXM_MODULE_HEAP_SIZE 512 +#endif + +// returns heap start address in R0 +// returns heap end address in R2 +// does not touch SP, it is already set up before the module runs + .global __user_setup_stackheap + .thumb_func +__user_setup_stackheap: + LDR r1, _txm_heap // load heap offset + LDR r2, =TXM_MODULE_HEAP_SIZE + ADDS r2, r2, r0 // calculate heap end address + BX lr + +// dummy main function + .global main + .thumb_func +main: + BX lr + + .align 8 +_txm_heap: + .zero TXM_MODULE_HEAP_SIZE diff --git a/ports_module/cortex_m0+/ac6/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m0+/ac6/module_lib/src/txm_module_thread_shell_entry.c new file mode 100644 index 00000000..f94e784f --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_lib/src/txm_module_thread_shell_entry.c @@ -0,0 +1,173 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef TXM_MODULE +#define TXM_MODULE +#endif + +#ifndef TX_SOURCE_CODE +#define TX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "txm_module.h" +#include "tx_thread.h" + +/* Define the global module entry pointer from the start thread of the module. */ + +TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; + + +/* Define the dispatch function pointer used in the module implementation. */ + +ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param_3); + + +/* Define the startup code that clears the uninitialized global data and sets up the + preset global variables. */ + +extern VOID _txm_module_initialize(VOID); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_thread_shell_entry Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the specified entry function of the thread. It */ +/* also provides a place for the thread's entry function to return. */ +/* If the thread returns, this function places the thread in a */ +/* "COMPLETED" state. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to current thread */ +/* thread_info Pointer to thread entry info */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_initialize cstartup initialization */ +/* thread_entry Thread's entry function */ +/* tx_thread_resume Resume the module callback thread */ +/* _txm_module_thread_system_suspend Module thread suspension routine */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) +{ + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + VOID (*entry_exit_notify)(TX_THREAD *, UINT); +#endif + + + /* Determine if this is the start thread. If so, we must prepare the module for + execution. If not, simply skip the C startup code. */ + if (thread_info -> txm_module_thread_entry_info_start_thread) + { + /* Initialize the C environment. */ + _txm_module_initialize(); + + /* Save the entry info pointer, for later use. */ + _txm_module_entry_info = thread_info; + + /* Save the kernel function dispatch address. This is used to make all resident calls from + the module. */ + _txm_module_kernel_call_dispatcher = thread_info -> txm_module_thread_entry_info_kernel_call_dispatcher; + + /* Ensure that we have a valid pointer. */ + while (!_txm_module_kernel_call_dispatcher) + { + /* Loop here, if an error is present getting the dispatch function pointer! + An error here typically indicates the resident portion of _tx_thread_schedule + is not supporting the trap to obtain the function pointer. */ + } + + /* Resume the module's callback thread, already created in the manager. */ + _txe_thread_resume(thread_info -> txm_module_thread_entry_info_callback_request_thread); + } + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + + /* Pickup the entry/exit application callback routine. */ + entry_exit_notify = thread_info -> txm_module_thread_entry_info_exit_notify; + + /* Determine if an application callback routine is specified. */ + if (entry_exit_notify != TX_NULL) + { + + /* Yes, notify application that this thread has been entered! */ + (entry_exit_notify)(thread_ptr, TX_THREAD_ENTRY); + } +#endif + + /* Call current thread's entry function. */ + (thread_info -> txm_module_thread_entry_info_entry) (thread_info -> txm_module_thread_entry_info_parameter); + + /* Suspend thread with a "completed" state. */ + + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + + /* Pickup the entry/exit application callback routine again. */ + entry_exit_notify = thread_info -> txm_module_thread_entry_info_exit_notify; + + /* Determine if an application callback routine is specified. */ + if (entry_exit_notify != TX_NULL) + { + + /* Yes, notify application that this thread has exited! */ + (entry_exit_notify)(thread_ptr, TX_THREAD_EXIT); + } +#endif + + /* Call actual thread suspension routine. */ + _txm_module_thread_system_suspend(thread_ptr); + +#ifdef TX_SAFETY_CRITICAL + + /* If we ever get here, raise safety critical exception. */ + TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); +#endif +} diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_restore.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_restore.S new file mode 100644 index 00000000..ccca2b62 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_restore.S @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_isr_exit +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_exit] Execution profiling ISR exit */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .thumb_func +_tx_thread_context_restore: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR exit function to indicate an ISR is complete. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_exit // Call the ISR exit function + POP {r0, r1} // Recover return address + MOV lr, r1 +#endif + + BX lr +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_save.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_save.S new file mode 100644 index 00000000..f55dc61c --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_context_save.S @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_isr_enter +#endif +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_enter] Execution profiling ISR enter */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .thumb_func +_tx_thread_context_save: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR enter function to indicate an ISR is starting. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_enter // Call the ISR enter function + POP {r0, r1} // Recover return address + MOV lr, r1 +#endif + + /* Context is already saved - just return. */ + + BX lr +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_control.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..f54505f0 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .thumb_func +_tx_thread_interrupt_control: + +/* Pickup current interrupt lockout posture. */ + + MRS r1, PRIMASK // Pickup current interrupt lockout + + +/* Apply the new interrupt posture. */ + + MSR PRIMASK, r0 // Apply the new interrupt lockout + MOV r0, r1 // Transfer old to return register + BX lr // Return to caller + +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_disable.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..0b9e4964 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_disable.S @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts and returning */ +/* the previous interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(VOID) +// { + .global _tx_thread_interrupt_disable + .thumb_func +_tx_thread_interrupt_disable: + +/* Pickup current interrupt lockout posture. */ + + MRS r0, PRIMASK + CPSID i + BX lr + +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_restore.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..b3d762cb --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_interrupt_restore.S @@ -0,0 +1,72 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring the previous */ +/* interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* previous_posture Previous interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_interrupt_restore(UINT previous_posture) +// { + .global _tx_thread_interrupt_restore + .thumb_func +_tx_thread_interrupt_restore: + /* Restore previous interrupt lockout posture. */ + MSR PRIMASK, r0 + BX lr +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_schedule.S new file mode 100644 index 00000000..f76fa5f0 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_schedule.S @@ -0,0 +1,564 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_thread_enter + .global _tx_execution_thread_exit +#endif +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global _tx_thread_schedule + .thumb_func +.type _tx_thread_schedule, function +_tx_thread_schedule: + /* This function should only ever be called on Cortex-M + from the first schedule request. Subsequent scheduling occurs + from the PendSV handling routine below. */ + + /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets. */ + MOVS r0, #0 // Build value for TX_FALSE + LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag + STR r0, [r2, #0] // Clear preempt disable flag + + /* Enable interrupts */ + CPSIE i + + /* Enter the scheduler for the first time. */ + LDR r0, =0x10000000 // Load PENDSVSET bit + LDR r1, =0xE000ED04 // Load ICSR address + STR r0, [r1] // Set PENDSVBIT in ICSR + DSB // Complete all memory accesses + ISB // Flush pipeline + + /* Wait here for the PendSV to take place. */ + +__tx_wait_here: + B __tx_wait_here // Wait for the PendSV to happen +// } + + + /* Memory Exception Handler. */ + .global HardFaultException + .thumb_func +HardFaultException: + .global MemManage_Handler + .global BusFault_Handler + .global UsageFault_Handler + .thumb_func +MemManage_Handler: + .thumb_func +BusFault_Handler: + .thumb_func +UsageFault_Handler: + + CPSID i // Disable interrupts + + /* Now pickup and store all the fault related information. */ + + LDR r2,=_txm_module_manager_memory_fault_info // Pickup fault info struct + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + STR r1, [r2, #0] // Save current thread pointer in fault info structure + MRS r0, CONTROL // Pickup current CONTROL register + STR r0, [r2, #24] // Save CONTROL + MRS r1, PSP // Pickup thread stack pointer + STR r1, [r2, #28] // Save thread stack pointer + LDR r0, [r1] // Pickup saved r0 (as r0-r3, r12, lr, pc, xpsr are automatically saved to stack) + STR r0, [r2, #32] // Save r0 + LDR r0, [r1, #4] // Pickup saved r1 + STR r0, [r2, #36] // Save r1 + LDR r0, [r1, #8] // Pickup saved r2 + STR r0, [r2, #40] // Save r2 + STR r3, [r2, #44] // Save r3 + STR r4, [r2, #48] // Save r4 + STR r5, [r2, #52] // Save r5 + STR r6, [r2, #56] // Save r6 + STR r7, [r2, #60] // Save r7 + MOV r0, r8 // Pickup current r8 register (high register) + STR r0, [r2, #64] // Save r8 + MOV r0, r9 // Pickup current r9 register + STR r0, [r2, #68] // Save r9 + MOV r0, r10 // Pickup current r10 register + STR r0, [r2, #72] // Save r10 + MOV r0, r11 // Pickup current r11 register + STR r0, [r2, #76] // Save r11 + LDR r0, [r1, #16] // Pickup saved r12 + STR r0, [r2, #80] // Save r12 + LDR r0, [r1, #20] // Pickup saved lr + STR r0, [r2, #84] // Save lr + LDR r0, [r1, #24] // Pickup instruction address at point of fault + STR r0, [r2, #4] // Save point of fault + LDR r0, [r1, #28] // Pickup xPSR + STR r0, [r2, #88] // Save xPSR + + MRS r0, CONTROL // Pickup current CONTROL register + MOVS r1, #1 + BICS r0, r0, r1 // Clear the UNPRIV bit + MSR CONTROL, r0 // Setup new CONTROL register + + BL _txm_module_manager_memory_fault_handler // Call memory manager fault handler + +#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. */ + CPSID i // Disable interrupts + BL _tx_execution_thread_exit // Call the thread exit function + CPSIE i // Enable interrupts +#endif + + MOVS 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 MemManage_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 + LDR r0, =0xFFFFFFFD // Exception return + MOV lr, r0 // Move exception return to lr + BX lr // Return from exception + + + /* Generic context switching PendSV handler. */ + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global PendSV_Handler + .global __tx_PendSVHandler + .syntax unified + .thumb_func +PendSV_Handler: + .thumb_func +__tx_PendSVHandler: + + /* Get current thread value and new thread pointer. */ + +__tx_ts_handler: + +#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. */ + CPSID i // Disable interrupts + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, r1} // Recover LR + MOV lr, r1 // + CPSIE i // Enable interrupts +#endif + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + MOVS r3, #0 // Build NULL value + LDR r1, [r0] // Pickup current thread pointer + + /* Determine if there is a current thread to finish preserving. */ + CMP r1, #0 + BEQ __tx_ts_new // If NULL, skip preservation + + /* Recover PSP and preserve current thread context. */ + + STR r3, [r0] // Set _tx_thread_current_ptr to NULL + MRS r3, PSP // Pickup PSP pointer (thread's stack pointer) + SUBS r3, r3, #16 // Allocate stack space + STM r3!, {r4-r7} // Save its remaining registers + MOV r4, r8 // Pick up r8 + MOV r5, r9 // Pick up r9 + MOV r6, r10 // Pick up r10 + MOV r7, r11 // Pick up r11 + SUBS r3, r3, #32 // Allocate stack space for r8-r11 + STM r3!, {r4-r7} // Save r8-r11 + SUBS r3, r3, #20 // Allocate stack space for lr + MOV r5, lr // Move lr into r5 + STR r5, [r3] // Save lr + STR r3, [r1, #8] // Save the thread stack pointer + + /* Determine if time-slice is active. If it isn't, skip time handling processing. */ + + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r4] // Pickup current time-slice + CMP r5, #0 + BEQ __tx_ts_new // If not active, skip processing + + /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable. */ + + STR r5, [r1, #24] // Save current time-slice + + /* Clear the global time-slice. */ + MOVS r3, #0 // Build NULL value + STR r3, [r4] // Clear time-slice + + /* Executing thread is now completely preserved!!! */ + +__tx_ts_new: + + /* Now we are looking for a new thread to execute! */ + + CPSID i // Disable interrupts + LDR r1, [r2] // Is there another thread ready to execute? + CMP r1, #0 + BNE __tx_ts_restore // Yes, schedule it + + /* The following is the idle wait processing... in this case, no threads are ready for execution and the + system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts + are disabled to allow use of WFI for waiting for a thread to arrive. */ + +__tx_ts_wait: + CPSID i // Disable interrupts + LDR r1, [r2] // Pickup the next thread to execute pointer + CMP r1, #0 + BNE __tx_ts_ready // If non-NULL, a new thread is ready! +#ifdef TX_ENABLE_WFI + DSB // Ensure no outstanding memory transactions + WFI // Wait for interrupt + ISB // Ensure pipeline is flushed +#endif + CPSIE i // Enable interrupts + B __tx_ts_wait // Loop to continue waiting + + /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are + already in the handler! */ + +__tx_ts_ready: + LDR r7, =0x08000000 // Build clear PendSV value + LDR r6, =0xE000ED04 // Build base NVIC address + STR r7, [r6] // Clear any PendSV + +__tx_ts_restore: + + /* A thread is ready, make the current thread the new thread + and enable interrupts. */ + + STR r1, [r0] // Setup the current thread pointer to the new thread + CPSIE i // Enable interrupts + + /* Increment the thread run count. */ + + LDR r7, [r1, #4] // Pickup the current thread run count + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r1, #24] // Pickup thread's current time-slice + ADDS r7, r7, #1 // Increment the thread run count + STR r7, [r1, #4] // Store the new run count + + /* Setup global time-slice with thread's current time-slice. */ + + STR r5, [r4] // Setup global time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread entry function to indicate the thread is executing. */ + PUSH {r0, r1} // Save r0 and r1 + BL _tx_execution_thread_enter // Call the thread execution enter function + POP {r0, r1} // Recover r0 and r1 +#endif + + /* Restore the thread context and PSP. */ + + LDR r3, [r1, #8] // Pickup thread's stack pointer + + // Set up CONTROL register based on user mode flag (privileged/unprivileged mode) + MRS r5, CONTROL // Pickup current CONTROL register + MOVS r6, #0x98 + LDR r4, [r1, r6] // Pickup current user mode flag + MOVS r6, #1 + BICS r5, r5, r6 // Clear the UNPRIV bit + ORRS r4, r4, r5 // Build new CONTROL register + MSR CONTROL, r4 // Setup new CONTROL register + + // Determine if MPU needs to be configured + LDR r0, =0xE000ED94 // Build MPU control reg address + MOVS r6, #0 // Build disable value + STR r6, [r0] // Disable MPU + MOVS r6, #0x90 + LDR r0, [r1, r6] // Pickup the module instance pointer + CMP r0, #0 + BEQ skip_mpu_setup // Is this thread owned by a module? No, skip MPU setup + MOV r8, r1 // Copy thread ptr + LDR r1, [r0, #0x64] // Pickup MPU register[0] + CMP r1, #0 + BEQ skip_mpu_setup // Is protection required for this module? No, skip MPU setup + LDR r1, =0xE000ED9C // Build address of MPU base register + + // Initialize loop to configure MPU registers + // Order doesn't matter, so txm_module_instance_mpu_registers[0] + // will be in region 7 and txm_module_instance_mpu_registers[7] will be in region 0. + MOVS r3, #0x64 // Index of MPU register settings in thread control block + ADD r0, r0, r3 // Build address of MPU register start in thread control block + MOVS r5, #0 // Select region 0 + LDR r4, =0xE000ED98 // Region register address + // Loop to load MPU registers +_tx_mpu_loop: + LDR r1, =0xE000ED9C // Build address of MPU base register + STR r5, [r4] // Set region + LDM r0!, {r2-r3} // Get MPU settings from the module + STM r1!, {r2-r3} // Set MPU registers for region + ADDS r5, r5, #1 // Increment to next region + CMP r5, #8 // Check if all regions have been set + BNE _tx_mpu_loop + + LDR r0, =0xE000ED94 // Build MPU control reg address + MOVS r1, #5 // Build enable value with background region enabled + STR r1, [r0] // Enable MPU + MOV r1, r8 // Get copied thread ptr + +skip_mpu_setup: + + // Restore the thread context and PSP + LDR r3, [r1, #8] // Pickup thread's stack pointer + LDR r5, [r3] // Recover saved LR + ADDS r3, r3, #4 // Position past LR + MOV lr, r5 // Restore LR + LDM r3!, {r4-r7} // Recover thread's registers (r8-r11) + MOV r11, r7 + MOV r10, r6 + MOV r9, r5 + MOV r8, r4 + LDM r3!, {r4-r7} // Recover thread's registers (r4-r7) + MSR PSP, r3 // Setup the thread's stack pointer + + BX lr // Return to thread! + + + /* SVC Handler. */ + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global SVC_Handler + .thumb_func +.type SVC_Handler, function +SVC_Handler: + .global __tx_SVCallHandler + .thumb_func +__tx_SVCallHandler: + + MRS r0, PSP // Pickup the PSP stack + LDR r1, [r0, #24] // Pickup the point of interrupt + MOV r3, r1 + SUBS r3, r3, #2 + LDRB r2, [r3] // Pickup the SVC parameter + + /* Determine which SVC trap we are processing */ + + CMP r2, #1 // Is it the entry into ThreadX? + BNE _tx_thread_user_return // No, return to user mode + + /* At this point we have an SVC 1, which means we are entering + the kernel from a module thread with user mode selected. */ + + LDR r2, =_txm_module_priv // Load address of where we should have come from + CMP r1, r2 // Did we come from user_mode_entry? + BEQ _tx_entry_continue // If no (not equal), then... + BX lr // return from where we came. +_tx_entry_continue: + LDR r3, [r0, #20] // This is the saved LR + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0 // Build clear value + MOVS r0, #0x98 // Index of current user mode + STR r1, [r2, r0] // Clear the current user mode selection for thread + MOVS r0, #0xA0 // Index of saved LR + STR r3, [r2, r0] // Save the original LR in thread control block + + /* If there is memory protection, use kernel stack */ + MOVS r0, #0x90 // Index of module instance ptr + LDR r0, [r2, r0] // Load the module instance ptr + LDR r0, [r0, #0x0C] // Load the module property flags + MOVS r1, #2 // MPU protection flag + TST r0, r1 // Check if memory protected + BEQ _tx_skip_kernel_stack_enter + + /* Switch to the module thread's kernel stack */ + MOVS r0, #0xA8 // Index of module kernel stack end + LDR r0, [r2, r0] // Load the module kernel stack end + MOVS r1, #0xA4 // Index of module kernel stack start + LDR r1, [r2, r1] // Load the module kernel stack start + +#ifndef TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE + MOVS r3, #0xAC // Index of module kernel stack size + LDR r3, [r2, r3] // Load the module kernel stack size + STR r1, [r2, #12] // Set stack start + STR r0, [r2, #16] // Set stack end + STR r3, [r2, #20] // Set stack size +#endif + + MRS r3, PSP // Pickup thread stack pointer + MOVS r1, #0xB0 // Index of module stack pointer + STR r3, [r2, r1] // Save thread stack pointer + + /* Build kernel stack by copying thread stack two registers at a time */ + SUBS r0, r0, #32 // Start at top of hardware stack + LDMIA r3!, {r1,r2} // Get r0, r1 from thread stack + STMIA r0!, {r1,r2} // Insert r0, r1 into kernel stack + LDMIA r3!, {r1,r2} // Get r2, r3 from thread stack + STMIA r0!, {r1,r2} // Insert r2, r3 into kernel stack + LDMIA r3!, {r1,r2} // Get r12, lr from thread stack + STMIA r0!, {r1,r2} // Insert r12, lr into kernel stack + LDMIA r3!, {r1,r2} // Get pc, xpsr from thread stack + STMIA r0!, {r1,r2} // Insert pc, xpsr into kernel stack + SUBS r0, r0, #32 // Go back to top of stack + + MSR PSP, r0 // Set kernel stack pointer + +_tx_skip_kernel_stack_enter: + MRS r0, CONTROL // Pickup current CONTROL register + MOVS r1, #1 + BICS r0, r0, r1 // Clear the UNPRIV bit + MSR CONTROL, r0 // Setup new CONTROL register + BX lr // Return to thread + +_tx_thread_user_return: + LDR r2, =_txm_module_user_mode_exit // Load address of where we should have come from + CMP r1, r2 // Did we come from user_mode_exit? + BEQ _tx_exit_continue // If no (not equal), then... + BX lr // return from where we came. +_tx_exit_continue: + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0x9C // Index of user mode + MOVS r3, #0x98 // Index of current user mode + LDR r1, [r2, r1] // Pick up user mode + STR r1, [r2, r3] // Set the current user mode selection for thread + + /* If there is memory protection, use kernel stack */ + MOVS r0, #0x90 // Index of module instance ptr + LDR r0, [r2, r0] // Load the module instance ptr + LDR r0, [r0, #0x0C] // Load the module property flags + MOVS r1, #2 // MPU protection flag + TST r0, r1 // Check if memory protected + BEQ _tx_skip_kernel_stack_exit + +#ifndef TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE + MOVS r0, #0xB4 // Index of module thread stack start + LDR r0, [r2, r0] // Load the module thread stack start + MOVS r1, #0xB8 // Index of module thread stack end + LDR r1, [r2, r1] // Load the module thread stack end + MOVS r3, #0xBC // Index of module thread stack size + LDR r3, [r2, r3] // Load the module thread stack size + STR r0, [r2, #12] // Set stack start + STR r1, [r2, #16] // Set stack end + STR r3, [r2, #20] // Set stack size +#endif + MOVS r1, #0xB0 // Index of module thread stack pointer + LDR r0, [r2, r1] // Load the module thread stack pointer + MRS r3, PSP // Pickup kernel stack pointer + + /* Copy kernel hardware stack to module thread stack. */ + LDM r3!, {r1-r2} // Get r0, r1 from kernel stack + STM r0!, {r1-r2} // Insert r0, r1 into thread stack + LDM r3!, {r1-r2} // Get r2, r3 from kernel stack + STM r0!, {r1-r2} // Insert r2, r3 into thread stack + LDM r3!, {r1-r2} // Get r12, lr from kernel stack + STM r0!, {r1-r2} // Insert r12, lr into thread stack + LDM r3!, {r1-r2} // Get pc, xpsr from kernel stack + STM r0!, {r1-r2} // Insert pc, xpsr into thread stack + SUBS r0, r0, #32 // Subtract 32 to get back to top of stack + MSR PSP, r0 // Set thread stack pointer + + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0x9C // Index of user mode + LDR r1, [r2, r1] // Pick up user mode + +_tx_skip_kernel_stack_exit: + MRS r0, CONTROL // Pickup current CONTROL register + ORRS r0, r0, r1 // OR in the user mode bit + MSR CONTROL, r0 // Setup new CONTROL register + BX lr // Return to thread + + /* Kernel entry function from user mode. */ + + .global _txm_module_manager_kernel_dispatch + .align 8 + .syntax unified +// VOID _txm_module_manager_user_mode_entry(VOID) +// { + .global _txm_module_manager_user_mode_entry + .thumb_func +_txm_module_manager_user_mode_entry: + SVC 1 // Enter kernel +_txm_module_priv: + /* At this point, we are out of user mode. The original LR has been saved in the + thread control block. Simply call the kernel dispatch function. */ + BL _txm_module_manager_kernel_dispatch + + /* Pickup the original LR value while still in privileged mode */ + LDR r2, =_tx_thread_current_ptr // Build current thread pointer address + LDR r3, [r2] // Pickup current thread pointer + MOVS r2, #0xA0 + LDR r1, [r3, r2] // Pickup saved LR from original call + MOV lr, r1 + + SVC 2 // Exit kernel and return to user mode +_txm_module_user_mode_exit: + BX lr // Return to the caller + NOP + NOP + NOP + NOP +// } + diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_stack_build.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_stack_build.S new file mode 100644 index 00000000..a86af1e4 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_stack_build.S @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .thumb_func +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + MOVS r3, #0x7 + BICS r2, r2, r3 // Align frame for 8-byte alignment + SUBS r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOVS r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r4 + STR r3, [r2, #8] // Store initial r5 + STR r3, [r2, #12] // Store initial r6 + STR r3, [r2, #16] // Store initial r7 + STR r3, [r2, #20] // Store initial r8 + STR r3, [r2, #24] // Store initial r9 + STR r3, [r2, #28] // Store initial r10 + STR r3, [r2, #32] // Store initial r11 + + /* Hardware stack follows. */ + + STR r3, [r2, #36] // Store initial r0 + STR r3, [r2, #40] // Store initial r1 + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + LDR r3, =0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + LDR r3, =0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's + // control block + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_system_return.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_system_return.S new file mode 100644 index 00000000..378f6e8d --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_thread_system_return.S @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .thumb_func +_tx_thread_system_return: + + /* Return to real scheduler via PendSV. Note that this routine is often + replaced with in-line assembly in tx_port.h to improved performance. */ + + LDR r0, =0x10000000 // Load PENDSVSET bit + LDR r1, =0xE000ED04 // Load NVIC base + STR r0, [r1] // Set PENDSVBIT in ICSR + MRS r0, IPSR // Pickup IPSR + CMP r0, #0 // Is it a thread returning? + BNE _isr_context // If ISR, skip interrupt enable + MRS r1, PRIMASK // Thread context returning, pickup PRIMASK + CPSIE i // Enable interrupts + MSR PRIMASK, r1 // Restore original interrupt posture +_isr_context: + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m0+/ac6/module_manager/src/tx_timer_interrupt.S new file mode 100644 index 00000000..e839aeac --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/tx_timer_interrupt.S @@ -0,0 +1,242 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* expiration functions are called. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .thumb_func +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR r1, =_tx_timer_system_clock // Pickup address of system clock + LDR r0, [r1, #0] // Pickup system clock + ADDS r0, r0, #1 // Increment system clock + STR r0, [r1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + // if (_tx_timer_time_slice) + // { + + LDR r3, =_tx_timer_time_slice // Pickup address of time-slice + LDR r2, [r3, #0] // Pickup time-slice + CMP r2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + // _tx_timer_time_slice--; + + SUBS r2, r2, #1 // Decrement the time-slice + STR r2, [r3, #0] // Store new time-slice value + + /* Check for expiration. */ + // if (__tx_timer_time_slice == 0) + + CMP r2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + // _tx_timer_expired_time_slice = TX_TRUE; + + LDR r3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOVS r0, #1 // Build expired value + STR r0, [r3, #0] // Set time-slice expiration flag + + // } + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR r1, =_tx_timer_current_ptr // Pickup current timer pointer address + LDR r0, [r1, #0] // Pickup current timer + LDR r2, [r0, #0] // Pickup timer list entry + CMP r2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR r3, =_tx_timer_expired // Pickup expiration flag address + MOVS r2, #1 // Build expired value + STR r2, [r3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADDS r0, r0, #4 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR r3, =_tx_timer_list_end // Pickup addr of timer list end + LDR r2, [r3, #0] // Pickup list end + CMP r0, r2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR r3, =_tx_timer_list_start // Pickup addr of timer list start + LDR r0, [r3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR r0, [r1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR r2, [r3, #0] // Pickup time-slice expired flag + CMP r2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR r1, =_tx_timer_expired // Pickup addr of other expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CMP r0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + PUSH {r0, lr} // Save the lr register on the stack + // and save r0 just to keep 8-byte alignment + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR r1, =_tx_timer_expired // Pickup addr of expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CMP r0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR r2, [r3, #0] // Pickup the actual flag + CMP r2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + LDR r0, =_tx_thread_preempt_disable // Build address of preempt disable flag + LDR r1, [r0] // Is the preempt disable flag set? + CMP r1, #0 // + BNE __tx_timer_skip_time_slice // Yes, skip the PendSV logic + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + LDR r3, [r2] // Pickup the execute thread pointer + LDR r0, =0xE000ED04 // Build address of control register + LDR r2, =0x10000000 // Build value for PendSV bit + CMP r1, r3 // Are they the same? + BEQ __tx_timer_skip_time_slice // If the same, there was no time-slice performed + STR r2, [r0] // Not the same, issue the PendSV for preemption +__tx_timer_skip_time_slice: + + // } + +__tx_timer_not_ts_expiration: + + POP {r0, r1} // Recover lr register (r0 is just there for + MOV lr, r1 // the 8-byte stack alignment + + // } + +__tx_timer_nothing_expired: + + DSB // Complete all memory access + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_alignment_adjust.c b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_alignment_adjust.c new file mode 100644 index 00000000..6c8b3b57 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_alignment_adjust.c @@ -0,0 +1,448 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_power_of_two_block_size Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates a power of two size at or immediately above*/ +/* the input size and returns it to the caller. */ +/* */ +/* INPUT */ +/* */ +/* size Block size */ +/* */ +/* OUTPUT */ +/* */ +/* calculated size Rounded up to power of two */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_alignment_adjust Adjust alignment for Cortex-M */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_power_of_two_block_size(ULONG size) +{ + /* Check for 0 size. */ + if(size == 0) + return 0; + + /* Minimum MPU block size is 256. */ + if(size <= 256) + return 256; + + /* Bit twiddling trick to round to next high power of 2 + (if original size is power of 2, it will return original size. Perfect!) */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + /* Return a power of 2 size at or above the input size. */ + return(size); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_alignment_adjust Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adjusts the alignment and size of the code and data */ +/* section for a given module implementation. */ +/* */ +/* INPUT */ +/* */ +/* module_preamble Pointer to module preamble */ +/* code_size Size of the code area (updated) */ +/* code_alignment Code area alignment (updated) */ +/* data_size Size of data area (updated) */ +/* data_alignment Data area alignment (updated) */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_power_of_two_block_size Calculate power of two size */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, + ULONG *code_size, + ULONG *code_alignment, + ULONG *data_size, + ULONG *data_alignment) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU +ULONG local_code_size; +ULONG local_code_alignment; +ULONG local_data_size; +ULONG local_data_alignment; +ULONG code_size_accum; +ULONG data_size_accum; + + /* Copy the input parameters into local variables for ease of use. */ + local_code_size = *code_size; + local_code_alignment = *code_alignment; + local_data_size = *data_size; + local_data_alignment = *data_alignment; + + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_code_alignment = _txm_power_of_two_block_size(local_code_size) >> 2; + code_size_accum = local_code_alignment + local_code_alignment; + code_size_accum = code_size_accum + (_txm_power_of_two_block_size(local_code_size - code_size_accum) >> 1); + code_size_accum = code_size_accum + _txm_power_of_two_block_size(local_code_size - code_size_accum); + local_code_size = code_size_accum; + + /* Determine data block sizes. Minimize the alignment requirement. + There are 4 MPU data entries available. The following is how the data size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to data size. + 2. 1/4 of the largest power of two that is greater than or equal to data size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_data_alignment = _txm_power_of_two_block_size(local_data_size) >> 2; + data_size_accum = local_data_alignment + local_data_alignment; + data_size_accum = data_size_accum + (_txm_power_of_two_block_size(local_data_size - data_size_accum) >> 1); + data_size_accum = data_size_accum + _txm_power_of_two_block_size(local_data_size - data_size_accum); + local_data_size = data_size_accum; + + /* Return all the information to the caller. */ + *code_size = local_code_size; + *code_alignment = local_code_alignment; + *data_size = local_data_size; + *data_alignment = local_data_alignment; + +#else + +ULONG local_code_size; +ULONG local_code_alignment; +ULONG local_data_size; +ULONG local_data_alignment; +ULONG code_block_size; +ULONG data_block_size; +ULONG code_size_accum; +ULONG data_size_accum; + + /* Copy the input parameters into local variables for ease of use. */ + local_code_size = *code_size; + local_code_alignment = *code_alignment; + local_data_size = *data_size; + local_data_alignment = *data_alignment; + + + /* Test for external memory enabled in preamble. */ + if(module_preamble -> txm_module_preamble_property_flags & TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS) + { + /* External/shared memory enabled. TXM_MODULE_MANAGER_CODE_MPU_ENTRIES-1 code entries will be used. */ + if (local_code_size <= (32*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 32 is best. */ + code_block_size = 32; + } + else if (local_code_size <= (64*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 64 is best. */ + code_block_size = 64; + } + else if (local_code_size <= (128*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 128 is best. */ + code_block_size = 128; + } + else if (local_code_size <= (256*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 256 is best. */ + code_block_size = 256; + } + else if (local_code_size <= (512*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 512 is best. */ + code_block_size = 512; + } + else if (local_code_size <= (1024*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 1024 is best. */ + code_block_size = 1024; + } + else if (local_code_size <= (2048*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 2048 is best. */ + code_block_size = 2048; + } + else if (local_code_size <= (4096*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 4096 is best. */ + code_block_size = 4096; + } + else if (local_code_size <= (8192*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 8192 is best. */ + code_block_size = 8192; + } + else if (local_code_size <= (16384*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 16384 is best. */ + code_block_size = 16384; + } + else if (local_code_size <= (32768*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 32768 is best. */ + code_block_size = 32768; + } + else if (local_code_size <= (65536*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 65536 is best. */ + code_block_size = 65536; + } + else if (local_code_size <= (131072*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 131072 is best. */ + code_block_size = 131072; + } + else if (local_code_size <= (262144*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 262144 is best. */ + code_block_size = 262144; + } + else if (local_code_size <= (524288*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 524288 is best. */ + code_block_size = 524288; + } + else if (local_code_size <= (1048576*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 1048576 is best. */ + code_block_size = 1048576; + } + else if (local_code_size <= (2097152*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 2097152 is best. */ + code_block_size = 2097152; + } + else if (local_code_size <= (4194304*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 4194304 is best. */ + code_block_size = 4194304; + } + else + { + /* Just set block size to 32MB just to create an allocation error! */ + code_block_size = 33554432; + } + + /* Calculate the new code size. */ + local_code_size = code_block_size*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1); + + /* Determine if the code block size is greater than the current alignment. If so, use block size + as the alignment. */ + if (code_block_size > local_code_alignment) + local_code_alignment = code_block_size; + + } + else + { + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_code_alignment = _txm_power_of_two_block_size(local_code_size) >> 2; + code_size_accum = local_code_alignment + local_code_alignment; + code_size_accum = code_size_accum + (_txm_power_of_two_block_size(local_code_size - code_size_accum) >> 1); + code_size_accum = code_size_accum + _txm_power_of_two_block_size(local_code_size - code_size_accum); + local_code_size = code_size_accum; + } + + /* Determine the best data block size, which in our case is the minimal alignment. */ + if (local_data_size <= (32*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 32 is best. */ + data_block_size = 32; + } + else if (local_data_size <= (64*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 64 is best. */ + data_block_size = 64; + } + else if (local_data_size <= (128*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 128 is best. */ + data_block_size = 128; + } + else if (local_data_size <= (256*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 256 is best. */ + data_block_size = 256; + } + else if (local_data_size <= (512*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 512 is best. */ + data_block_size = 512; + } + else if (local_data_size <= (1024*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 1024 is best. */ + data_block_size = 1024; + } + else if (local_data_size <= (2048*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 2048 is best. */ + data_block_size = 2048; + } + else if (local_data_size <= (4096*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 4096 is best. */ + data_block_size = 4096; + } + else if (local_data_size <= (8192*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 8192 is best. */ + data_block_size = 8192; + } + else if (local_data_size <= (16384*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 16384 is best. */ + data_block_size = 16384; + } + else if (local_data_size <= (32768*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 32768 is best. */ + data_block_size = 32768; + } + else if (local_data_size <= (65536*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 65536 is best. */ + data_block_size = 65536; + } + else if (local_data_size <= (131072*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 131072 is best. */ + data_block_size = 131072; + } + else if (local_data_size <= (262144*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 262144 is best. */ + data_block_size = 262144; + } + else if (local_data_size <= (524288*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 524288 is best. */ + data_block_size = 524288; + } + else if (local_data_size <= (1048576*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 1048576 is best. */ + data_block_size = 1048576; + } + else if (local_data_size <= (2097152*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 2097152 is best. */ + data_block_size = 2097152; + } + else if (local_data_size <= (4194304*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 4194304 is best. */ + data_block_size = 4194304; + } + else + { + /* Just set data block size to 32MB just to create an allocation error! */ + data_block_size = 33554432; + } + + /* Calculate the new data size. */ + data_size_accum = data_block_size; + while(data_size_accum < local_data_size) + { + data_size_accum += data_block_size; + } + local_data_size = data_size_accum; + + /* Determine if the data block size is greater than the current alignment. If so, use block size + as the alignment. */ + if (data_block_size > local_data_alignment) + { + local_data_alignment = data_block_size; + } + + /* Return all the information to the caller. */ + *code_size = local_code_size; + *code_alignment = local_code_alignment; + *data_size = local_data_size; + *data_alignment = local_data_alignment; + +#endif +} diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_external_memory_enable.c b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_external_memory_enable.c new file mode 100644 index 00000000..1386362a --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_external_memory_enable.c @@ -0,0 +1,295 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_mutex.h" +#include "tx_queue.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_external_memory_enable Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates an entry in the MPU table for a shared */ +/* memory space. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* start_address Start address of memory */ +/* length Length of external memory */ +/* attributes Memory attributes (r/w) */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_mutex_get Get protection mutex */ +/* _tx_mutex_put Release protection mutex */ +/* _txm_power_of_two_block_size Round length to power of two */ +/* _txm_module_manager_mm_register_setup Reconfigure MPU registers */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_external_memory_enable(TXM_MODULE_INSTANCE *module_instance, + VOID *start_address, + ULONG length, + UINT attributes) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU +ULONG block_size; +ULONG region_size; +ULONG srd_bits; +ULONG size_register; +ULONG address; +ULONG shared_index; +ULONG attributes_check = 0; + + /* Determine if the module manager has not been initialized yet. */ + if (_txm_module_manager_ready != TX_TRUE) + { + /* Module manager has not been initialized. */ + return(TX_NOT_AVAILABLE); + } + + /* Determine if the module is valid. */ + if (module_instance == TX_NULL) + { + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Get module manager protection mutex. */ + _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); + + /* Determine if the module instance is valid. */ + if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Determine if the module instance is in the loaded state. */ + if (module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if the module is not ready. */ + return(TX_START_ERROR); + } + + /* Determine if there are shared memory entries available. */ + if(module_instance -> txm_module_instance_shared_memory_count >= TXM_MODULE_MPU_SHARED_ENTRIES) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* No more entries available. */ + return(TX_NO_MEMORY); + } + + /* Start address and length must adhere to Cortex-M7 MPU. + The address must align with the block size. */ + + block_size = _txm_power_of_two_block_size(length); + address = (ULONG) start_address; + if(address != (address & ~(block_size - 1))) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return alignment error. */ + return(TXM_MODULE_ALIGNMENT_ERROR); + } + + /* At this point, we have a valid address and block size. + Set up MPU registers. */ + + /* Pick up index into shared memory entries. */ + shared_index = TXM_MODULE_MPU_SHARED_INDEX + module_instance -> txm_module_instance_shared_memory_count; + + /* Save address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[shared_index].txm_module_mpu_region_address = address | shared_index | 0x10; + + /* Calculate the region size. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Calculate the subregion bits. */ + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, length); + + /* Generate SRD, size, and enable attributes. */ + size_register = srd_bits | region_size | TXM_ENABLE_REGION | TXM_MODULE_MPU_SHARED_ACCESS_CONTROL; + + /* Check for optional write attribute. */ + if(attributes & TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE) + { + attributes_check = TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT; + } + + /* Save attribute-size register. */ + module_instance -> txm_module_instance_mpu_registers[shared_index].txm_module_mpu_region_attribute_size = attributes_check | size_register; + + /* Keep track of shared memory address and length in module instance. */ + module_instance -> txm_module_instance_shared_memory_address[module_instance -> txm_module_instance_shared_memory_count] = address; + module_instance -> txm_module_instance_shared_memory_length[module_instance -> txm_module_instance_shared_memory_count] = length; + + /* Increment counter. */ + module_instance -> txm_module_instance_shared_memory_count++; + + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return success. */ + return(TX_SUCCESS); + +#else + +ULONG block_size; +ULONG region_size; +ULONG subregion_bits; +ULONG address; +UINT attributes_check = 0; +TXM_MODULE_PREAMBLE *module_preamble; + + /* Determine if the module manager has not been initialized yet. */ + if (_txm_module_manager_ready != TX_TRUE) + { + /* Module manager has not been initialized. */ + return(TX_NOT_AVAILABLE); + } + + /* Determine if the module is valid. */ + if (module_instance == TX_NULL) + { + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Get module manager protection mutex. */ + _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); + + /* Determine if the module instance is valid. */ + if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Determine if the module instance is in the loaded state. */ + if (module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if the module is not ready. */ + return(TX_START_ERROR); + } + + /* Check if preamble shared mem and mem protection property bits are set. */ + module_preamble = module_instance -> txm_module_instance_preamble_ptr; + if((module_preamble -> txm_module_preamble_property_flags & (TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS)) + != (TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS)) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if bit not set. */ + return(TXM_MODULE_INVALID_PROPERTIES); + } + + /* Start address and length must adhere to Cortex-M MPU. + The address must align with the block size. */ + + block_size = _txm_power_of_two_block_size(length); + address = (ULONG) start_address; + if(address != (address & ~(block_size - 1))) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return alignment error. */ + return(TXM_MODULE_ALIGNMENT_ERROR); + } + + /* At this point, we have a valid address and block size. + Set up MPU registers. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX] = address | TXM_MODULE_MANAGER_SHARED_MPU_REGION | 0x10; + + /* Calculate the region size. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + /* Calculate the subregion bits. */ + subregion_bits = _txm_module_manager_calculate_srd_bits(block_size, length); + + /* Check for valid attributes. */ + if(attributes & TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE) + { + attributes_check = TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT; + } + + /* Build register with attributes. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX+1] = region_size | subregion_bits | attributes_check | 0x12070001; + + /* Keep track of shared memory address and length in module instance. */ + module_instance -> txm_module_instance_shared_memory_address = address; + module_instance -> txm_module_instance_shared_memory_length = length; + + /* Recalculate MPU settings. */ + _txm_module_manager_mm_register_setup(module_instance); + + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return success. */ + return(TX_SUCCESS); + +#endif +} diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_handler.c b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_handler.c new file mode 100644 index 00000000..a00a3325 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_handler.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/* Define the user's fault notification callback function pointer. This is + setup via the txm_module_manager_memory_fault_notify API. */ + +VOID (*_txm_module_manager_fault_notify)(TX_THREAD *, TXM_MODULE_INSTANCE *); + + +/* Define a macro that can be used to allocate global variables useful to + store information about the last fault. This macro is defined in + txm_module_port.h and is usually populated in the assembly language + fault handling prior to the code calling _txm_module_manager_memory_fault_handler. */ + +TXM_MODULE_MANAGER_FAULT_INFO + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_memory_fault_handler Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles a fault associated with a memory protected */ +/* module. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_terminate Terminate thread */ +/* */ +/* CALLED BY */ +/* */ +/* Fault handler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_memory_fault_handler(VOID) +{ + +TXM_MODULE_INSTANCE *module_instance_ptr; +TX_THREAD *thread_ptr; + + /* Pickup the current thread. */ + thread_ptr = _tx_thread_current_ptr; + + /* Initialize the module instance pointer to NULL. */ + module_instance_ptr = TX_NULL; + + /* Is there a thread? */ + if (thread_ptr) + { + /* Pickup the module instance. */ + module_instance_ptr = thread_ptr -> tx_thread_module_instance_ptr; + + /* Terminate the current thread. */ + _tx_thread_terminate(_tx_thread_current_ptr); + } + + /* Determine if there is a user memory fault notification callback. */ + if (_txm_module_manager_fault_notify) + { + /* Yes, call the user's notification memory fault callback. */ + (_txm_module_manager_fault_notify)(thread_ptr, module_instance_ptr); + } +} diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_notify.c b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_notify.c new file mode 100644 index 00000000..3c44ee61 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_memory_fault_notify.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/* Define the external user's fault notification callback function pointer. This is + setup via the txm_module_manager_memory_fault_notify API. */ + +extern VOID (*_txm_module_manager_fault_notify)(TX_THREAD *, TXM_MODULE_INSTANCE *); + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_memory_fault_notify Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers an application callback when/if a memory */ +/* fault occurs. The supplied thread is automatically terminated, but */ +/* any other threads in the same module may still execute. */ +/* */ +/* INPUT */ +/* */ +/* notify_function Memory fault notification */ +/* function, NULL disables. */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_memory_fault_notify(VOID (*notify_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +{ + /* Setup notification function. */ + _txm_module_manager_fault_notify = notify_function; + + /* Return success. */ + return(TX_SUCCESS); +} diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_mm_register_setup.c b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_mm_register_setup.c new file mode 100644 index 00000000..9882e1ff --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_mm_register_setup.c @@ -0,0 +1,797 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_region_size_get Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts the region size in bytes to the block size */ +/* for the Cortex-M0+ MPU specification. */ +/* */ +/* INPUT */ +/* */ +/* block_size Size of the block in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* MPU size specification */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_mm_register_setup */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_module_manager_region_size_get(ULONG block_size) +{ + +ULONG return_value; + + /* Process relative to the input block size. */ + if (block_size == 32) + { + return_value = 0x04; + } + else if (block_size == 64) + { + return_value = 0x05; + } + else if (block_size == 128) + { + return_value = 0x06; + } + else if (block_size == 256) + { + return_value = 0x07; + } + else if (block_size == 512) + { + return_value = 0x08; + } + else if (block_size == 1024) + { + return_value = 0x09; + } + else if (block_size == 2048) + { + return_value = 0x0A; + } + else if (block_size == 4096) + { + return_value = 0x0B; + } + else if (block_size == 8192) + { + return_value = 0x0C; + } + else if (block_size == 16384) + { + return_value = 0x0D; + } + else if (block_size == 32768) + { + return_value = 0x0E; + } + else if (block_size == 65536) + { + return_value = 0x0F; + } + else if (block_size == 131072) + { + return_value = 0x10; + } + else if (block_size == 262144) + { + return_value = 0x11; + } + else if (block_size == 524288) + { + return_value = 0x12; + } + else if (block_size == 1048576) + { + return_value = 0x13; + } + else if (block_size == 2097152) + { + return_value = 0x14; + } + else + { + /* Max 4MB MPU pages for modules. */ + return_value = 0x15; + } + + return(return_value); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_calculate_srd_bits Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates the SRD bits that need to be set to */ +/* protect "length" bytes in a block. */ +/* */ +/* INPUT */ +/* */ +/* block_size Size of the block in bytes */ +/* length Actual length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* SRD bits to be OR'ed with region attribute register. */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_mm_register_setup */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length) +{ + +ULONG srd_bits = 0; +UINT srd_bit_index; + + /* length is smaller than block_size, set SRD bits if block_size is 256 or more. */ + if((block_size >= 256) && (length < block_size)) + { + /* Divide block_size by 8 by shifting right 3. Result is size of subregion. */ + block_size = block_size >> 3; + + /* Set SRD index into attribute register. */ + srd_bit_index = 8; + + /* If subregion overlaps length, move to the next subregion. */ + while(length > block_size) + { + length = length - block_size; + srd_bit_index++; + } + /* Check for a portion of code remaining. */ + if(length) + { + srd_bit_index++; + } + + /* Set unused subregion bits. */ + while(srd_bit_index < 16) + { + srd_bits = srd_bits | (0x1 << srd_bit_index); + srd_bit_index++; + } + } + + return(srd_bits); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_mm_register_setup Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets up the MPU register definitions based on the */ +/* module's memory characteristics. */ +/* */ +/* Default MPU layout: */ +/* Entry Description */ +/* 0 Kernel mode entry */ +/* 1 Module code region */ +/* 2 Module code region */ +/* 3 Module code region */ +/* 4 Module code region */ +/* 5 Module data region */ +/* 6 Module data region */ +/* 7 Module data region */ +/* */ +/* If TXM_MODULE_MANAGER_16_MPU is defined, there are 16 MPU slots. */ +/* MPU layout for the Cortex-M7: */ +/* Entry Description */ +/* 0 Kernel mode entry */ +/* 1 Module code region */ +/* 2 Module code region */ +/* 3 Module code region */ +/* 4 Module code region */ +/* 5 Module data region */ +/* 6 Module data region */ +/* 7 Module data region */ +/* 8 Module data region */ +/* 9 Module shared memory region */ +/* 10 Module shared memory region */ +/* 11 Module shared memory region */ +/* 12 Unused region */ +/* 13 Unused region */ +/* 14 Unused region */ +/* 15 Unused region */ +/* */ +/* */ +/* INPUT */ +/* */ +/* module_instance Pointer to module instance */ +/* */ +/* OUTPUT */ +/* */ +/* MPU specifications for module in module_instance */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_region_size_get */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_thread_create */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU + +ULONG code_address; +ULONG code_size; +ULONG data_address; +ULONG data_size; +ULONG start_stop_stack_size; +ULONG callback_stack_size; +ULONG block_size; +ULONG region_size; +ULONG srd_bits = 0; +UINT mpu_table_index; +UINT i; + + + /* Setup the first MPU region for kernel mode entry. */ + /* Set address register to user mode entry function address, which is guaranteed to be at least 256-byte aligned. + Mask address to proper range, region 0, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_address = ((ULONG) _txm_module_manager_user_mode_entry & 0xFFFFFFE0) | 0x10; + /* Set the attributes, size (256 bytes) and enable bit. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_CODE_ACCESS_CONTROL | (_txm_module_manager_region_size_get(256) << 1) | TXM_ENABLE_REGION; + /* End of kernel mode entry setup. */ + + /* Setup code protection. */ + + /* Initialize the MPU table index. */ + mpu_table_index = 1; + + /* Pickup code starting address and actual size. */ + code_address = (ULONG) module_instance -> txm_module_instance_code_start; + code_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size; + + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MPU_CODE_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to code size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(code_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from code_size to calculate remaining space. */ + code_size = code_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(code_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + code_size = code_size - block_size; + block_size = _txm_power_of_two_block_size(code_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = (code_address & ~(block_size - 1)) | mpu_table_index | 0x10; + /* Build the attribute-size register with permissions, SRD, size, enable. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_CODE_ACCESS_CONTROL | srd_bits | region_size | TXM_ENABLE_REGION; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Increment MPU table index. */ + mpu_table_index++; + } + /* End of code protection. */ + + /* Setup data protection. */ + + /* Reset SRD bitfield. */ + srd_bits = 0; + + /* Pickup data starting address and actual size. */ + data_address = (ULONG) module_instance -> txm_module_instance_data_start; + + /* Adjust the size of the module elements to be aligned to the default alignment. We do this + so that when we partition the allocated memory, we can simply place these regions right beside + each other without having to align their pointers. Note this only works when they all have + the same alignment. */ + + data_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size; + start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size; + callback_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size; + + data_size = ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + start_stop_stack_size = ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + callback_stack_size = ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + /* Update the data size to include thread stacks. */ + data_size = data_size + start_stop_stack_size + callback_stack_size; + + /* Determine data block sizes. Minimize the alignment requirement. + There are 4 MPU data entries available. The following is how the data size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to data size. + 2. 1/4 of the largest power of two that is greater than or equal to data size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the data area. */ + for (i = 0; i < TXM_MODULE_MPU_DATA_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to data size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(data_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from data_size to calculate remaining space. */ + data_size = data_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(data_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + data_size = data_size - block_size; + block_size = _txm_power_of_two_block_size(data_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size); + } + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = (data_address & ~(block_size - 1)) | mpu_table_index | 0x10; + /* Build the attribute-size register with permissions, SRD, size, enable. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_DATA_ACCESS_CONTROL | srd_bits | region_size | TXM_ENABLE_REGION; + + /* Adjust the data address. */ + data_address = data_address + block_size; + + /* Increment MPU table index. */ + mpu_table_index++; + } + + /* Setup MPU for the remaining regions. */ + while (mpu_table_index < TXM_MODULE_MPU_TOTAL_ENTRIES) + { + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = mpu_table_index | 0x10; + + /* Increment MPU table index. */ + mpu_table_index++; + } + +#else + +ULONG code_address; +ULONG code_size; +ULONG data_address; +ULONG data_size; +ULONG start_stop_stack_size; +ULONG callback_stack_size; +ULONG block_size; +ULONG base_address_register; +ULONG base_attribute_register; +ULONG region_size; +ULONG srd_bits = 0; +UINT mpu_register = 0; +UINT mpu_table_index; +UINT i; + + + /* Setup the first region for the ThreadX trampoline code. */ + /* Set base register to user mode entry, which is guaranteed to be at least 256-byte aligned. */ + base_address_register = (ULONG) _txm_module_manager_user_mode_entry; + + /* Mask address to proper range, region 0, set Valid bit. */ + base_address_register = (base_address_register & 0xFFFFFF00) | mpu_register | 0x10; + module_instance -> txm_module_instance_mpu_registers[0] = base_address_register; + + /* Attributes: read only, write-back, shareable, size 256 bytes, region enabled. */ + module_instance -> txm_module_instance_mpu_registers[1] = 0x0607000F; + + /* Initialize the MPU register. */ + mpu_register = 1; + + /* Initialize the MPU table index. */ + mpu_table_index = 2; + + /* Setup values for code area. */ + code_address = (ULONG) module_instance -> txm_module_instance_code_start; + code_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size; + + /* Check if shared memory was set up. If so, only 3 entries are available for + code protection. If not set up, 4 code entries are available. */ + if(module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX] == 0) + { + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MANAGER_CODE_MPU_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to code size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(code_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from code_size to calculate remaining space. */ + code_size = code_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(code_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + code_size = code_size - block_size; + block_size = _txm_power_of_two_block_size(code_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Build the base address register. */ + base_address_register = (code_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x06070001; + + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + } + + /* Only 3 code entries available. */ + else + { + /* Calculate block size, one code entry taken up by shared memory. */ + block_size = _txm_power_of_two_block_size(code_size / (TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1)); + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1; i++) + { + /* Build the base address register. */ + base_address_register = (code_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Check if SRD bits need to be set. */ + if (code_size < block_size) + { + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x06070000; + + /* Is there still some code? If so set the region enable bit. */ + if (code_size) + { + /* Set the region enable bit. */ + base_attribute_register = base_attribute_register | 0x1; + } + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Decrement the code size. */ + if (code_size > block_size) + { + code_size = code_size - block_size; + } + else + { + code_size = 0; + } + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + + /* Adjust indeces to pass over the shared memory entry. */ + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + + /* Setup values for data area. */ + data_address = (ULONG) module_instance -> txm_module_instance_data_start; + + /* Adjust the size of the module elements to be aligned to the default alignment. We do this + so that when we partition the allocated memory, we can simply place these regions right beside + each other without having to align their pointers. Note this only works when they all have + the same alignment. */ + + data_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size; + start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size; + callback_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size; + + data_size = ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + start_stop_stack_size = ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + callback_stack_size = ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + /* Update the data size to include thread stacks. */ + data_size = data_size + start_stop_stack_size + callback_stack_size; + + block_size = _txm_power_of_two_block_size(data_size / TXM_MODULE_MANAGER_DATA_MPU_ENTRIES); + + /* Reset SRD bitfield. */ + srd_bits = 0; + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Now loop through to setup MPU protection for the data area. */ + for (i = 0; i < TXM_MODULE_MANAGER_DATA_MPU_ENTRIES; i++) + { + /* Build the base address register. */ + base_address_register = (data_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Check if SRD bits need to be set. */ + if (data_size < block_size) + { + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size); + } + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x13070000; + + /* Is there still some data? If so set the region enable bit. */ + if (data_size) + { + /* Set the region enable bit. */ + base_attribute_register = base_attribute_register | 0x1; + } + + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the data address. */ + data_address = data_address + block_size; + + /* Decrement the data size. */ + if (data_size > block_size) + { + data_size = data_size - block_size; + } + else + { + data_size = 0; + } + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + +#endif +} + +#ifdef TXM_MODULE_MANAGER_16_MPU +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_inside_data_check Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if the specified object is inside shared */ +/* memory. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Pointer to module instance */ +/* obj_ptr Pointer to the object */ +/* obj_size Size of the object */ +/* */ +/* OUTPUT */ +/* */ +/* Whether the object is inside the shared memory region. */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Module dispatch check functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size) +{ + +UINT shared_memory_index; +UINT num_shared_memory_mpu_entries; +ALIGN_TYPE shared_memory_address_start; +ALIGN_TYPE shared_memory_address_end; + + /* Check for overflow. */ + if ((obj_ptr) > ((obj_ptr) + (obj_size))) + { + return(TX_FALSE); + } + + /* Check if the object is inside the module data. */ + if ((obj_ptr >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && + ((obj_ptr + obj_size) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1))) + { + return(TX_TRUE); + } + + /* Check if the object is inside the shared memory. */ + num_shared_memory_mpu_entries = module_instance -> txm_module_instance_shared_memory_count; + for (shared_memory_index = 0; shared_memory_index < num_shared_memory_mpu_entries; shared_memory_index++) + { + + shared_memory_address_start = (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address[shared_memory_index]; + shared_memory_address_end = shared_memory_address_start + module_instance -> txm_module_instance_shared_memory_length[shared_memory_index]; + + if ((obj_ptr >= (ALIGN_TYPE) shared_memory_address_start) && + ((obj_ptr + obj_size) <= (ALIGN_TYPE) shared_memory_address_end)) + { + return(TX_TRUE); + } + } + + return(TX_FALSE); +} +#endif diff --git a/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_thread_stack_build.S b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_thread_stack_build.S new file mode 100644 index 00000000..5f3aefe6 --- /dev/null +++ b/ports_module/cortex_m0+/ac6/module_manager/src/txm_module_manager_thread_stack_build.S @@ -0,0 +1,140 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_thread_stack_build Cortex-M0+/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to shell function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _txm_module_manager_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +// { + .global _txm_module_manager_thread_stack_build + .thumb_func +_txm_module_manager_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + MOVS r3, #0x7 // + BICS r2, r2, r3 // Align frame for 8-byte alignment + SUBS r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOVS r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r8 + STR r3, [r2, #12] // Store initial r10 + STR r3, [r2, #16] // Store initial r11 + STR r3, [r2, #20] // Store initial r4 + STR r3, [r2, #24] // Store initial r5 + STR r3, [r2, #28] // Store initial r6 + STR r3, [r2, #32] // Store initial r7 + + /* Hardware stack follows. */ + + STR r0, [r2, #36] // Store initial r0, which is the thread control block + + LDR r3, [r0, #8] // Pickup thread entry info pointer,which is in the stack pointer position of the thread control block. + // It was setup in the txm_module_manager_thread_create function. It will be overwritten later in this + // function with the actual, initial stack pointer. + STR r3, [r2, #40] // Store initial r1, which is the module entry information. + LDR r3, [r3, #8] // Pickup data base register from the module information + STR r3, [r2, #8] // Store initial r9 (data base register) + MOVS r3, #0 // Clear r3 again + + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + LDR r3, =0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + LDR r3, =0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's control block + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/.cproject b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..63be6be1 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/.project b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..725328bd --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/.project @@ -0,0 +1,27 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.arm.debug.ds.nature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/cortexm_crt0.s b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/cortexm_crt0.s new file mode 100644 index 00000000..d4cb1636 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/cortexm_crt0.s @@ -0,0 +1,127 @@ + .global _start + .extern main + + + .section .init, "ax" + .code 16 + .align 2 + .thumb_func + + +_start: + CPSID i + ldr r1, =__stack_end__ + mov sp, r1 + + + /* Copy initialised sections into RAM if required. */ + ldr r0, =__data_load_start__ + ldr r1, =__data_start__ + ldr r2, =__data_end__ + bl crt0_memory_copy + ldr r0, =__text_load_start__ + ldr r1, =__text_start__ + ldr r2, =__text_end__ + bl crt0_memory_copy + ldr r0, =__fast_load_start__ + ldr r1, =__fast_start__ + ldr r2, =__fast_end__ + bl crt0_memory_copy + ldr r0, =__ctors_load_start__ + ldr r1, =__ctors_start__ + ldr r2, =__ctors_end__ + bl crt0_memory_copy + ldr r0, =__dtors_load_start__ + ldr r1, =__dtors_start__ + ldr r2, =__dtors_end__ + bl crt0_memory_copy + ldr r0, =__rodata_load_start__ + ldr r1, =__rodata_start__ + ldr r2, =__rodata_end__ + bl crt0_memory_copy + + + /* Zero bss. */ + ldr r0, =__bss_start__ + ldr r1, =__bss_end__ + mov r2, #0 + bl crt0_memory_set + + + /* Setup heap - not recommended for Threadx but here for compatibility reasons */ + ldr r0, = __heap_start__ + ldr r1, = __heap_end__ + sub r1, r1, r0 + mov r2, #0 + str r2, [r0] + add r0, r0, #4 + str r1, [r0] + + + /* constructors in case of using C++ */ + ldr r0, =__ctors_start__ + ldr r1, =__ctors_end__ +crt0_ctor_loop: + cmp r0, r1 + beq crt0_ctor_end + ldr r2, [r0] + add r0, #4 + push {r0-r1} + blx r2 + pop {r0-r1} + b crt0_ctor_loop +crt0_ctor_end: + + + /* Setup call frame for main() */ + mov r0, #0 + mov lr, r0 + mov r12, sp + + +start: + /* Jump to main() */ + mov r0, #0 + mov r1, #0 + ldr r2, =main + blx r2 + /* when main returns, loop forever. */ +crt0_exit_loop: + b crt0_exit_loop + + + + /* Startup helper functions. */ + + +crt0_memory_copy: + cmp r0, r1 + beq memory_copy_done + sub r2, r2, r1 + beq memory_copy_done +memory_copy_loop: + ldrb r3, [r0] + add r0, r0, #1 + strb r3, [r1] + add r1, r1, #1 + sub r2, r2, #1 + bne memory_copy_loop +memory_copy_done: + bx lr + + +crt0_memory_set: + cmp r0, r1 + beq memory_set_done + strb r2, [r0] + add r0, r0, #1 + b crt0_memory_set +memory_set_done: + bx lr + + + /* Setup attibutes of stack and heap sections so they don't take up room in the elf file */ + .section .stack, "wa", %nobits + .section .stack_process, "wa", %nobits + .section .heap, "wa", %nobits + \ No newline at end of file diff --git a/ports/cortex_a8/green/example_build/sample_threadx.c b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.c similarity index 98% rename from ports/cortex_a8/green/example_build/sample_threadx.c rename to ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.c index 418ec634..597f373c 100644 --- a/ports/cortex_a8/green/example_build/sample_threadx.c +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.c @@ -26,6 +26,7 @@ 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... */ @@ -71,7 +72,7 @@ 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", first_unused_memory, DEMO_BYTE_POOL_SIZE); + 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. */ diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..1dab0b65 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..585bf94b --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,203 @@ +MEMORY +{ + UNPLACED_SECTIONS (wx) : ORIGIN = 0x100000000, LENGTH = 0 + AHB_Peripherals (wx) : ORIGIN = 0x50000000, LENGTH = 0x00200000 + APB1_Peripherals (wx) : ORIGIN = 0x40080000, LENGTH = 0x00080000 + APB0_Peripherals (wx) : ORIGIN = 0x40000000, LENGTH = 0x00080000 + GPIO (wx) : ORIGIN = 0x2009c000, LENGTH = 0x00004000 + AHBSRAM1 (wx) : ORIGIN = 0x20080000, LENGTH = 0x00004000 + AHBSRAM0 (wx) : ORIGIN = 0x2007c000, LENGTH = 0x00004000 + RAM (wx) : ORIGIN = 0x20000000, LENGTH = 0x00020000 + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00080000 +} + + +SECTIONS +{ + __AHB_Peripherals_segment_start__ = 0x50000000; + __AHB_Peripherals_segment_end__ = 0x50200000; + __APB1_Peripherals_segment_start__ = 0x40080000; + __APB1_Peripherals_segment_end__ = 0x40100000; + __APB0_Peripherals_segment_start__ = 0x40000000; + __APB0_Peripherals_segment_end__ = 0x40080000; + __GPIO_segment_start__ = 0x2009c000; + __GPIO_segment_end__ = 0x200a0000; + __AHBSRAM1_segment_start__ = 0x20080000; + __AHBSRAM1_segment_end__ = 0x20084000; + __AHBSRAM0_segment_start__ = 0x2007c000; + __AHBSRAM0_segment_end__ = 0x20080000; + __RAM_segment_start__ = 0x20000000; + __RAM_segment_end__ = 0x20008000; + __FLASH_segment_start__ = 0x00000000; + __FLASH_segment_end__ = 0x00030000; + + __STACKSIZE__ = 1024; + __STACKSIZE_PROCESS__ = 0; + __STACKSIZE_IRQ__ = 0; + __STACKSIZE_FIQ__ = 0; + __STACKSIZE_SVC__ = 0; + __STACKSIZE_ABT__ = 0; + __STACKSIZE_UND__ = 0; + __HEAPSIZE__ = 128; + + __vectors_load_start__ = __FLASH_segment_start__; + .vectors __FLASH_segment_start__ : AT(__FLASH_segment_start__) + { + __vectors_start__ = .; + *(.vectors .vectors.*) + } + __vectors_end__ = __vectors_start__ + SIZEOF(.vectors); + + . = ASSERT(__vectors_end__ >= __FLASH_segment_start__ && __vectors_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .vectors is too large to fit in FLASH memory segment"); + + __init_load_start__ = ALIGN(__vectors_end__ , 4); + .init ALIGN(__vectors_end__ , 4) : AT(ALIGN(__vectors_end__ , 4)) + { + __init_start__ = .; + *(.init .init.*) + } + __init_end__ = __init_start__ + SIZEOF(.init); + + . = ASSERT(__init_end__ >= __FLASH_segment_start__ && __init_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .init is too large to fit in FLASH memory segment"); + + __text_load_start__ = ALIGN(__init_end__ , 4); + .text ALIGN(__init_end__ , 4) : AT(ALIGN(__init_end__ , 4)) + { + __text_start__ = .; + *(.text .text.* .glue_7t .glue_7 .gnu.linkonce.t.* .gcc_except_table) + } + __text_end__ = __text_start__ + SIZEOF(.text); + + . = ASSERT(__text_end__ >= __FLASH_segment_start__ && __text_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .text is too large to fit in FLASH memory segment"); + + __dtors_load_start__ = ALIGN(__text_end__ , 4); + .dtors ALIGN(__text_end__ , 4) : AT(ALIGN(__text_end__ , 4)) + { + __dtors_start__ = .; + KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) + } + __dtors_end__ = __dtors_start__ + SIZEOF(.dtors); + + . = ASSERT(__dtors_end__ >= __FLASH_segment_start__ && __dtors_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .dtors is too large to fit in FLASH memory segment"); + + __ctors_load_start__ = ALIGN(__dtors_end__ , 4); + .ctors ALIGN(__dtors_end__ , 4) : AT(ALIGN(__dtors_end__ , 4)) + { + __ctors_start__ = .; + KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) + } + __ctors_end__ = __ctors_start__ + SIZEOF(.ctors); + + . = ASSERT(__ctors_end__ >= __FLASH_segment_start__ && __ctors_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .ctors is too large to fit in FLASH memory segment"); + + __rodata_load_start__ = ALIGN(__ctors_end__ , 4); + .rodata ALIGN(__ctors_end__ , 4) : AT(ALIGN(__ctors_end__ , 4)) + { + __rodata_start__ = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + __rodata_end__ = __rodata_start__ + SIZEOF(.rodata); + + . = ASSERT(__rodata_end__ >= __FLASH_segment_start__ && __rodata_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .rodata is too large to fit in FLASH memory segment"); + + __fast_load_start__ = ALIGN(__rodata_end__ , 4); + .fast ALIGN(__RAM_segment_start__ , 4) : AT(ALIGN(__rodata_end__ , 4)) + { + __fast_start__ = .; + *(.fast .fast.*) + } + __fast_end__ = __fast_start__ + SIZEOF(.fast); + + __fast_load_end__ = __fast_load_start__ + SIZEOF(.fast); + + . = ASSERT((__fast_load_start__ + SIZEOF(.fast)) >= __FLASH_segment_start__ && (__fast_load_start__ + SIZEOF(.fast)) <= (__FLASH_segment_start__ + 0x00030000) , "error: .fast is too large to fit in FLASH memory segment"); + + .fast_run ALIGN(__RAM_segment_start__ , 4) (NOLOAD) : + { + __fast_run_start__ = .; + . = MAX(__fast_run_start__ + SIZEOF(.fast), .); + } + __fast_run_end__ = __fast_run_start__ + SIZEOF(.fast_run); + + . = ASSERT(__fast_run_end__ >= __RAM_segment_start__ && __fast_run_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .fast_run is too large to fit in RAM memory segment"); + + __data_load_start__ = ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4); + .data ALIGN(__fast_run_end__ , 4) : AT(ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4)) + { + __data_start__ = .; + *(.data .data.* .gnu.linkonce.d.*) + } + __data_end__ = __data_start__ + SIZEOF(.data); + + __data_load_end__ = __data_load_start__ + SIZEOF(.data); + + __FLASH_segment_used_end__ = ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4) + SIZEOF(.data); + + . = ASSERT((__data_load_start__ + SIZEOF(.data)) >= __FLASH_segment_start__ && (__data_load_start__ + SIZEOF(.data)) <= (__FLASH_segment_start__ + 0x00030000) , "error: .data is too large to fit in FLASH memory segment"); + + .data_run ALIGN(__fast_run_end__ , 4) (NOLOAD) : + { + __data_run_start__ = .; + . = MAX(__data_run_start__ + SIZEOF(.data), .); + } + __data_run_end__ = __data_run_start__ + SIZEOF(.data_run); + + . = ASSERT(__data_run_end__ >= __RAM_segment_start__ && __data_run_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .data_run is too large to fit in RAM memory segment"); + + __bss_load_start__ = ALIGN(__data_run_end__ , 4); + .bss ALIGN(__data_run_end__ , 4) (NOLOAD) : AT(ALIGN(__data_run_end__ , 4)) + { + __bss_start__ = .; + *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) + } + __bss_end__ = __bss_start__ + SIZEOF(.bss); + + . = ASSERT(__bss_end__ >= __RAM_segment_start__ && __bss_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .bss is too large to fit in RAM memory segment"); + + __non_init_load_start__ = ALIGN(__bss_end__ , 4); + .non_init ALIGN(__bss_end__ , 4) (NOLOAD) : AT(ALIGN(__bss_end__ , 4)) + { + __non_init_start__ = .; + *(.non_init .non_init.*) + } + __non_init_end__ = __non_init_start__ + SIZEOF(.non_init); + + . = ASSERT(__non_init_end__ >= __RAM_segment_start__ && __non_init_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .non_init is too large to fit in RAM memory segment"); + + __heap_load_start__ = ALIGN(__non_init_end__ , 4); + .heap ALIGN(__non_init_end__ , 4) (NOLOAD) : AT(ALIGN(__non_init_end__ , 4)) + { + __heap_start__ = .; + *(.heap) + . = ALIGN(MAX(__heap_start__ + __HEAPSIZE__ , .), 4); + } + __heap_end__ = __heap_start__ + SIZEOF(.heap); + + . = ASSERT(__heap_end__ >= __RAM_segment_start__ && __heap_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .heap is too large to fit in RAM memory segment"); + + __stack_load_start__ = ALIGN(__heap_end__ , 4); + .stack ALIGN(__heap_end__ , 4) (NOLOAD) : AT(ALIGN(__heap_end__ , 4)) + { + __stack_start__ = .; + *(.stack) + . = ALIGN(MAX(__stack_start__ + __STACKSIZE__ , .), 4); + } + __stack_end__ = __stack_start__ + SIZEOF(.stack); + + . = ASSERT(__stack_end__ >= __RAM_segment_start__ && __stack_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .stack is too large to fit in RAM memory segment"); + + __stack_process_load_start__ = ALIGN(__stack_end__ , 4); + .stack_process ALIGN(__stack_end__ , 4) (NOLOAD) : AT(ALIGN(__stack_end__ , 4)) + { + __stack_process_start__ = .; + *(.stack_process) + . = ALIGN(MAX(__stack_process_start__ + __STACKSIZE_PROCESS__ , .), 4); + } + __stack_process_end__ = __stack_process_start__ + SIZEOF(.stack_process); + + __RAM_segment_used_end__ = ALIGN(__stack_end__ , 4) + SIZEOF(.stack_process); + + . = ASSERT(__stack_process_end__ >= __RAM_segment_start__ && __stack_process_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .stack_process is too large to fit in RAM memory segment"); + +} + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_initialize_low_level.S b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_initialize_low_level.S new file mode 100644 index 00000000..502809df --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_initialize_low_level.S @@ -0,0 +1,240 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global __RAM_segment_used_end__ + .global _tx_timer_interrupt + .global __main + .global _vectors + .global __tx_NMIHandler // NMI + .global __tx_BadHandler // HardFault + .global __tx_SVCallHandler // SVCall + .global __tx_DBGHandler // Monitor + .global __tx_PendSVHandler // PendSV + .global __tx_SysTickHandler // SysTick + .global __tx_IntHandler // Int 0 + + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M0+/GNU */ +/* 6.1.10 */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .thumb_func +_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 + ADDS r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + + LDR r0, =0xE000ED08 // Build address of NVIC registers + LDR r1, =_vectors // Pickup address of vector table + STR r1, [r0] // Set vector table address + + /* 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 + + /* Enable the cycle count register. */ + + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + MOVS r2, #1 + ORRS r1, r1, r2 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ + + LDR r0, =0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + MOVS r2, #0x14 + STR r1, [r0, r2] // Setup SysTick Reload Value + MOVS r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + LDR r2, =0xD18 + STR r1, [r0, r2] // Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + LDR r2, =0xD1C + STR r1, [r0, r2] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + LDR r2, =0xD20 + STR r1, [r0, r2] // 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. */ + + .global __tx_BadHandler + .thumb_func +__tx_BadHandler: + B __tx_BadHandler + +/* added to catch the hardfault */ + + .global __tx_HardfaultHandler + .thumb_func +__tx_HardfaultHandler: + B __tx_HardfaultHandler + +/* Generic interrupt handler template */ + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + + /* Do interrupt handler work here */ + /* BL .... */ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + +/* System Tick timer interrupt handler */ + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + + +/* NMI, DBG handlers */ + .global __tx_NMIHandler + .thumb_func +__tx_NMIHandler: + B __tx_NMIHandler + + .global __tx_DBGHandler + .thumb_func +__tx_DBGHandler: + B __tx_DBGHandler + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_enter + .thumb_func +_tx_execution_isr_enter: + BX LR +#endif + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_exit + .thumb_func +_tx_execution_isr_exit: + BX LR +#endif diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_simulator_startup.s b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_simulator_startup.s new file mode 100644 index 00000000..73692924 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_simulator_startup.s @@ -0,0 +1,73 @@ + + .syntax unified + .section .vectors, "ax" + .code 16 + .align 0 + .global _vectors + +_vectors: + .word __stack_end__ + .word reset_handler + .word __tx_NMIHandler + .word __tx_HardfaultHandler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word __tx_SVCallHandler //_SVC_Handler - used by Threadx scheduler // + .word __tx_DBGHandler + .word 0 // Reserved + .word __tx_PendSVHandler + .word __tx_SysTickHandler // Used by Threadx timer functionality + .word __tx_BadHandler // Populate with user Interrupt handler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + + + + .section .init, "ax" + .global reset_handler + .thumb_func +reset_handler: + +// low level hardware config, such as PLL setup goes here + + b _start + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.cproject b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.cproject new file mode 100644 index 00000000..9dc7b2bb --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.cproject @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.project b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.project new file mode 100644 index 00000000..17f6aba3 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/.project @@ -0,0 +1,27 @@ + + + sample_threadx_module + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.arm.debug.ds.nature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/gcc_setup.s b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/gcc_setup.s new file mode 100644 index 00000000..f055d1c5 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/gcc_setup.s @@ -0,0 +1,131 @@ + + .text + .align 4 + .syntax unified + + .global _gcc_setup + .thumb_func +_gcc_setup: + + /* Store other preserved registers. */ + push {r3-r7} + mov r8, lr + + ldr r3, =__FLASH_segment_start__ + ldr r4, =__RAM_segment_start__ + mov r5,r0 + + /* Copy GOT table. */ + + ldr r0, =__got_load_start__ + subs r0,r0,r3 + add r0,r0,r5 + ldr r1, =__new_got_start__ + subs r1,r1, r4 + add r1,r1,r9 + ldr r2, =__new_got_end__ + subs r2,r2,r4 + add r2,r2,r9 + +new_got_setup: + cmp r1, r2 // See if there are more GOT entries + beq got_setup_done // No, done with GOT setup + ldr r6, [r0] // Pickup current GOT entry + cmp r6, #0 // Is it 0? + beq address_built // Yes, just skip the adjustment + cmp r6, r4 // Is it in the code or data area? + blt flash_area // If less than, it is a code address + subs r6, r6, r4 // Compute offset of data area + add r6, r6, r9 // Build address based on the loaded data address + b address_built // Finished building address +flash_area: + subs r6, r6, r3 // Compute offset of code area + add r6, r6, r5 // Build address based on the loaded code address +address_built: + str r6, [r1] // Store in new GOT table + adds r0, r0, #4 // Move to next entry + adds r1, r1, #4 // + b new_got_setup // Continue at the top of the loop +got_setup_done: + + + /* Copy initialised sections into RAM if required. */ + + ldr r0, =__data_load_start__ + subs r0,r0,r3 + add r0,r0,r5 + ldr r1, =__data_start__ + subs r1,r1, r4 + add r1,r1,r9 + ldr r2, =__data_end__ + subs r2,r2,r4 + add r2,r2,r9 + bl crt0_memory_copy + + /* Zero bss. */ + + ldr r0, =__bss_start__ + subs r0,r0,r4 + add r0,r0,r9 + ldr r1, =__bss_end__ + subs r1,r1,r4 + add r1,r1,r9 + movs r2, #0 + bl crt0_memory_set + + + /* Setup heap - not recommended for Threadx but here for compatibility reasons */ + + ldr r0, =__heap_start__ + subs r0,r0,r4 + add r0,r0,r9 + ldr r1, =__heap_end__ + subs r1,r1,r4 + add r1,r1,r9 + subs r1,r1,r0 + movs r2, #0 + str r2, [r0] + adds r0, r0, #4 + str r1, [r0] + + /* Store other preserved registers. */ + pop {r3-r7} + mov lr, r8 + bx lr // Return to caller + + .align 4 + + /* Startup helper functions. */ + + .thumb_func +crt0_memory_copy: + + cmp r0, r1 + beq memory_copy_done + cmp r2, r1 + beq memory_copy_done + subs r2, r2, r1 +memory_copy_loop: + ldrb r3, [r0] + adds r0, r0, #1 + strb r3, [r1] + adds r1, r1, #1 + subs r2, r2, #1 + cmp r2, #0 + bne memory_copy_loop +memory_copy_done: + bx lr + + .thumb_func +crt0_memory_set: + cmp r0, r1 + beq memory_set_done + strb r2, [r0] + adds r0, r0, #1 + b crt0_memory_set +memory_set_done: + bx lr + + /* Setup attibutes of heap section so it doesn't take up room in the elf file */ + .section .heap, "wa", %nobits + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.c b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.c new file mode 100644 index 00000000..f2647144 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.c @@ -0,0 +1,432 @@ +/* This is a small demo of the high-performance ThreadX kernel running as a module. It includes + examples of eight threads of different priorities, using a message queue, semaphore, mutex, + event flags group, byte pool, and block pool. */ + +/* Specify that this is a module! */ + +#define TXM_MODULE + + +/* Include the ThreadX module definitions. */ + +#include "txm_module.h" + + +/* Define constants. */ + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the external memory area. */ + +#define EXTERNAL_MEMORY_SIZE (64 * 1024) +#define EXTERNAL_MEMORY (0x80000) + + +/* Define the pool space in the bss section of the module. ULONG is used to + get the word alignment. */ + +ULONG demo_module_pool_space[DEMO_BYTE_POOL_SIZE / 4]; + + +/* 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; + + +/* 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; +ULONG semaphore_0_puts; +ULONG event_0_sets; +ULONG queue_0_sends; + +/* 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); + +void semaphore_0_notify(TX_SEMAPHORE *semaphore_ptr) +{ + + if (semaphore_ptr == semaphore_0) + semaphore_0_puts++; +} + + +void event_0_notify(TX_EVENT_FLAGS_GROUP *event_flag_group_ptr) +{ + + if (event_flag_group_ptr == event_flags_0) + event_0_sets++; +} + + +void queue_0_notify(TX_QUEUE *queue_ptr) +{ + + if (queue_ptr == queue_0) + queue_0_sends++; +} + + +/* Define the module start function. */ + +void demo_module_start(ULONG id) +{ + +CHAR *pointer; + + /* Allocate all the objects. In MPU mode, modules cannot allocate control blocks within + their own memory area so they cannot corrupt the resident portion of ThreadX by overwriting + the control block(s). */ + txm_module_object_allocate((void*)&thread_0, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_1, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_2, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_3, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_4, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_5, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_6, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_7, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&queue_0, sizeof(TX_QUEUE)); + txm_module_object_allocate((void*)&semaphore_0, sizeof(TX_SEMAPHORE)); + txm_module_object_allocate((void*)&mutex_0, sizeof(TX_MUTEX)); + txm_module_object_allocate((void*)&event_flags_0, sizeof(TX_EVENT_FLAGS_GROUP)); + txm_module_object_allocate((void*)&byte_pool_0, sizeof(TX_BYTE_POOL)); + txm_module_object_allocate((void*)&block_pool_0, sizeof(TX_BLOCK_POOL)); + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(byte_pool_0, "module byte pool 0", demo_module_pool_space, 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + tx_queue_send_notify(queue_0, queue_0_notify); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(semaphore_0, "module semaphore 0", 1); + + tx_semaphore_put_notify(semaphore_0, semaphore_0_notify); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(event_flags_0, "module event flags 0"); + + tx_event_flags_set_notify(event_flags_0, event_0_notify); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(mutex_0, "module 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, "module 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) + { + /* Test external memory sharing. */ + *(ULONG *)EXTERNAL_MEMORY = 0xABABABAB; + + /* 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_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.ld b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.ld new file mode 100644 index 00000000..1efc6ca1 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/sample_threadx_module.ld @@ -0,0 +1,215 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00030000, LENGTH = 0x00010000 + RAM (wx) : ORIGIN = 0x00041800, LENGTH = 0x00010000 +} + + +SECTIONS +{ + __FLASH_segment_start__ = 0x00030000; + __FLASH_segment_end__ = 0x00040000; + __RAM_segment_start__ = 0x00041800; + __RAM_segment_end__ = 0x00051800; + + __HEAPSIZE__ = 128; + + __preamble_load_start__ = __FLASH_segment_start__; + .preamble __FLASH_segment_start__ : AT(__FLASH_segment_start__) + { + __preamble_start__ = .; +/* *(.preamble .preamble.*) */ + } + __preamble_end__ = __preamble_start__ + SIZEOF(.preamble); + + __dynsym_load_start__ = ALIGN(__preamble_end__ , 4); + .dynsym ALIGN(__dynsym_load_start__ , 4) : AT(ALIGN(__dynsym_load_start__ , 4)) + { + . = ALIGN(4); + KEEP (*(.dynsym)) + KEEP (*(.dynsym*)) + . = ALIGN(4); + } + __dynsym_end__ = __dynsym_load_start__ + SIZEOF(.dynsym); + + __dynstr_load_start__ = ALIGN(__dynsym_end__ , 4); + .dynstr ALIGN(__dynstr_load_start__ , 4) : AT(ALIGN(__dynstr_load_start__, 4)) + { + . = ALIGN(4); + KEEP (*(.dynstr)) + KEEP (*(.dynstr*)) + . = ALIGN(4); + } + __dynstr_end__ = __dynstr_load_start__ + SIZEOF(.dynstr); + + __reldyn_load_start__ = ALIGN(__dynstr_end__ , 4); + .rel.dyn ALIGN(__reldyn_load_start__ , 4) : AT(ALIGN(__reldyn_load_start__ , 4)) + { + . = ALIGN(4); + KEEP (*(.rel.dyn)) + KEEP (*(.rel.dyn*)) + . = ALIGN(4); + } + __reldyn_end__ = __reldyn_load_start__ + SIZEOF(.rel.dyn); + + __relplt_load_start__ = ALIGN(__reldyn_end__ , 4); + .rel.plt ALIGN(__relplt_load_start__ , 4) : AT(ALIGN(__relplt_load_start__ , 4)) + { + . = ALIGN(4); + KEEP (*(.rel.plt)) + KEEP (*(.rel.plt*)) + . = ALIGN(4); + } + __relplt_end__ = __relplt_load_start__ + SIZEOF(.rel.plt); + + __plt_load_start__ = ALIGN(__relplt_end__ , 4); + .plt ALIGN(__plt_load_start__ , 4) : AT(ALIGN(__plt_load_start__ , 4)) + { + . = ALIGN(4); + KEEP (*(.plt)) + KEEP (*(.plt*)) + . = ALIGN(4); + } + __plt_end__ = __plt_load_start__ + SIZEOF(.plt); + + __interp_load_start__ = ALIGN(__plt_end__ , 4); + .interp ALIGN(__interp_load_start__ , 4) : AT(ALIGN(__interp_load_start__ , 4)) + { + . = ALIGN(4); + KEEP (*(.interp)) + KEEP (*(.interp*)) + . = ALIGN(4); + } + __interp_end__ = __interp_load_start__ + SIZEOF(.interp); + + __hash_load_start__ = ALIGN(__interp_end__ , 4); + .hash ALIGN(__hash_load_start__ , 4) : AT(ALIGN(__hash_load_start__, 4)) + { + . = ALIGN(4); + KEEP (*(.hash)) + KEEP (*(.hash*)) + . = ALIGN(4); + } + __hash_end__ = __hash_load_start__ + SIZEOF(.hash); + + __text_load_start__ = ALIGN(__hash_end__ , 4); + .text ALIGN(__text_load_start__ , 4) : AT(ALIGN(__text_load_start__, 4)) + { + __text_start__ = .; + + __preamble_start__ = .; + *(.preamble .preamble.*) + __preamble_end__ = __preamble_start__ + 0x80; + + *(.text .text.* .glue_7t .glue_7 .gnu.linkonce.t.* .gcc_except_table ) + } + __text_end__ = __text_start__ + SIZEOF(.text); + + __dtors_load_start__ = ALIGN(__text_end__ , 4); + .dtors ALIGN(__text_end__ , 4) : AT(ALIGN(__text_end__ , 4)) + { + __dtors_start__ = .; + KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) + } + __dtors_end__ = __dtors_start__ + SIZEOF(.dtors); + + __ctors_load_start__ = ALIGN(__dtors_end__ , 4); + .ctors ALIGN(__dtors_end__ , 4) : AT(ALIGN(__dtors_end__ , 4)) + { + __ctors_start__ = .; + KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) + } + __ctors_end__ = __ctors_start__ + SIZEOF(.ctors); + + __got_load_start__ = ALIGN(__ctors_end__ , 4); + .got ALIGN(__ctors_end__ , 4) : AT(ALIGN(__ctors_end__ , 4)) + { + . = ALIGN(4); + _sgot = .; + KEEP (*(.got)) + KEEP (*(.got*)) + . = ALIGN(4); + _egot = .; + } + __got_end__ = __got_load_start__ + SIZEOF(.got); + + __rodata_load_start__ = ALIGN(__got_end__ , 4); + .rodata ALIGN(__got_end__ , 4) : AT(ALIGN(__got_end__ , 4)) + { + __rodata_start__ = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + __rodata_end__ = __rodata_start__ + SIZEOF(.rodata); + + __code_size__ = SIZEOF(.data) + __rodata_end__ - __FLASH_segment_start__; + + __fast_load_start__ = ALIGN(__rodata_end__ , 4); + + __fast_load_end__ = __fast_load_start__ + SIZEOF(.fast); + + __new_got_start__ = ALIGN(__RAM_segment_start__ , 4); + + __new_got_end__ = __new_got_start__ + SIZEOF(.got); + + .fast ALIGN(__new_got_end__ , 4) : AT(ALIGN(__rodata_end__ , 4)) + { + __fast_start__ = .; + *(.fast .fast.*) + } + __fast_end__ = __fast_start__ + SIZEOF(.fast); + + .fast_run ALIGN(__fast_end__ , 4) (NOLOAD) : + { + __fast_run_start__ = .; + . = MAX(__fast_run_start__ + SIZEOF(.fast), .); + } + __fast_run_end__ = __fast_run_start__ + SIZEOF(.fast_run); + + __data_load_start__ = ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4); + .data ALIGN(__fast_run_end__ , 4) : AT(ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4)) + { + __data_start__ = .; + *(.data .data.* .gnu.linkonce.d.*) + } + __data_end__ = __data_start__ + SIZEOF(.data); + + __data_load_end__ = __data_load_start__ + SIZEOF(.data); + + __FLASH_segment_used_end__ = ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4) + SIZEOF(.data); + + .data_run ALIGN(__fast_run_end__ , 4) (NOLOAD) : + { + __data_run_start__ = .; + . = MAX(__data_run_start__ + SIZEOF(.data), .); + } + __data_run_end__ = __data_run_start__ + SIZEOF(.data_run); + + __bss_load_start__ = ALIGN(__data_run_end__ , 4); + .bss ALIGN(__data_run_end__ , 4) (NOLOAD) : AT(ALIGN(__data_run_end__ , 4)) + { + __bss_start__ = .; + *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) + } + __bss_end__ = __bss_start__ + SIZEOF(.bss); + + __non_init_load_start__ = ALIGN(__bss_end__ , 4); + .non_init ALIGN(__bss_end__ , 4) (NOLOAD) : AT(ALIGN(__bss_end__ , 4)) + { + __non_init_start__ = .; + *(.non_init .non_init.*) + } + __non_init_end__ = __non_init_start__ + SIZEOF(.non_init); + + __heap_load_start__ = ALIGN(__non_init_end__ , 4); + .heap ALIGN(__non_init_end__ , 4) (NOLOAD) : AT(ALIGN(__non_init_end__ , 4)) + { + __heap_start__ = .; + *(.heap) + . = ALIGN(MAX(__heap_start__ + __HEAPSIZE__ , .), 4); + } + __heap_end__ = __heap_start__ + SIZEOF(.heap); + + __data_size__ = __heap_end__ - __RAM_segment_start__; + +} + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/txm_module_preamble.S b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/txm_module_preamble.S new file mode 100644 index 00000000..dec92093 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module/txm_module_preamble.S @@ -0,0 +1,60 @@ + .text + .align 4 + .syntax unified + .section .preamble + + // Define public symbols + .global __txm_module_preamble + .global __txm_module_preamble_two + + // Define application-specific start/stop entry points for the module + .global demo_module_start + + // Define common external references + .global _txm_module_thread_shell_entry + .global _txm_module_callback_request_thread_entry + +__txm_module_preamble: + .dc.l 0x4D4F4455 // Module ID + .dc.l 0x6 // Module Major Version + .dc.l 0x1 // Module Minor Version + .dc.l 32 // Module Preamble Size in 32-bit words + .dc.l 0x12345678 // Module ID (application defined) + .dc.l 0x02000007 // Module Properties where: + // Bits 31-24: Compiler ID + // 0 -> IAR + // 1 -> ARM + // 2 -> GNU + // Bit 0: 0 -> Privileged mode execution + // 1 -> User mode execution + // Bit 1: 0 -> No MPU protection + // 1 -> MPU protection (must have user mode selected) + // Bit 2: 0 -> Disable shared/external memory access + // 1 -> Enable shared/external memory access + .dc.l _txm_module_thread_shell_entry - . - 0 // Module Shell Entry Point + .dc.l demo_module_start - . - 0 // Module Start Thread Entry Point + .dc.l 0 // Module Stop Thread Entry Point + .dc.l 1 // Module Start/Stop Thread Priority + .dc.l 1024 // Module Start/Stop Thread Stack Size + .dc.l _txm_module_callback_request_thread_entry - . - 0 // Module Callback Thread Entry + .dc.l 1 // Module Callback Thread Priority + .dc.l 1024 // Module Callback Thread Stack Size + .dc.l __code_size__ // Module Code Size + .dc.l __data_size__ // Module Data Size + .dc.l 0 // Reserved 0 + .dc.l 0 // Reserved 1 + .dc.l 0 // Reserved 2 + .dc.l 0 // Reserved 3 + .dc.l 0 // Reserved 4 + .dc.l 0 // Reserved 5 + .dc.l 0 // Reserved 6 + .dc.l 0 // Reserved 7 + .dc.l 0 // Reserved 8 + .dc.l 0 // Reserved 9 + .dc.l 0 // Reserved 10 + .dc.l 0 // Reserved 11 + .dc.l 0 // Reserved 12 + .dc.l 0 // Reserved 13 + .dc.l 0 // Reserved 14 + .dc.l 0 // Reserved 15 + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.cproject b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.cproject new file mode 100644 index 00000000..c243c1fd --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.cproject @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.project b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.project new file mode 100644 index 00000000..f1ca14fa --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/.project @@ -0,0 +1,27 @@ + + + sample_threadx_module_manager + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.arm.debug.ds.nature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/cortexm_crt0.s b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/cortexm_crt0.s new file mode 100644 index 00000000..d4cb1636 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/cortexm_crt0.s @@ -0,0 +1,127 @@ + .global _start + .extern main + + + .section .init, "ax" + .code 16 + .align 2 + .thumb_func + + +_start: + CPSID i + ldr r1, =__stack_end__ + mov sp, r1 + + + /* Copy initialised sections into RAM if required. */ + ldr r0, =__data_load_start__ + ldr r1, =__data_start__ + ldr r2, =__data_end__ + bl crt0_memory_copy + ldr r0, =__text_load_start__ + ldr r1, =__text_start__ + ldr r2, =__text_end__ + bl crt0_memory_copy + ldr r0, =__fast_load_start__ + ldr r1, =__fast_start__ + ldr r2, =__fast_end__ + bl crt0_memory_copy + ldr r0, =__ctors_load_start__ + ldr r1, =__ctors_start__ + ldr r2, =__ctors_end__ + bl crt0_memory_copy + ldr r0, =__dtors_load_start__ + ldr r1, =__dtors_start__ + ldr r2, =__dtors_end__ + bl crt0_memory_copy + ldr r0, =__rodata_load_start__ + ldr r1, =__rodata_start__ + ldr r2, =__rodata_end__ + bl crt0_memory_copy + + + /* Zero bss. */ + ldr r0, =__bss_start__ + ldr r1, =__bss_end__ + mov r2, #0 + bl crt0_memory_set + + + /* Setup heap - not recommended for Threadx but here for compatibility reasons */ + ldr r0, = __heap_start__ + ldr r1, = __heap_end__ + sub r1, r1, r0 + mov r2, #0 + str r2, [r0] + add r0, r0, #4 + str r1, [r0] + + + /* constructors in case of using C++ */ + ldr r0, =__ctors_start__ + ldr r1, =__ctors_end__ +crt0_ctor_loop: + cmp r0, r1 + beq crt0_ctor_end + ldr r2, [r0] + add r0, #4 + push {r0-r1} + blx r2 + pop {r0-r1} + b crt0_ctor_loop +crt0_ctor_end: + + + /* Setup call frame for main() */ + mov r0, #0 + mov lr, r0 + mov r12, sp + + +start: + /* Jump to main() */ + mov r0, #0 + mov r1, #0 + ldr r2, =main + blx r2 + /* when main returns, loop forever. */ +crt0_exit_loop: + b crt0_exit_loop + + + + /* Startup helper functions. */ + + +crt0_memory_copy: + cmp r0, r1 + beq memory_copy_done + sub r2, r2, r1 + beq memory_copy_done +memory_copy_loop: + ldrb r3, [r0] + add r0, r0, #1 + strb r3, [r1] + add r1, r1, #1 + sub r2, r2, #1 + bne memory_copy_loop +memory_copy_done: + bx lr + + +crt0_memory_set: + cmp r0, r1 + beq memory_set_done + strb r2, [r0] + add r0, r0, #1 + b crt0_memory_set +memory_set_done: + bx lr + + + /* Setup attibutes of stack and heap sections so they don't take up room in the elf file */ + .section .stack, "wa", %nobits + .section .stack_process, "wa", %nobits + .section .heap, "wa", %nobits + \ No newline at end of file diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/libgcc.a b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/libgcc.a new file mode 100644 index 0000000000000000000000000000000000000000..b2d68d687c1bca75608bbb991eab68c73225a150 GIT binary patch literal 1525572 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 __FLASH_segment_start__ && __vectors_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .vectors is too large to fit in FLASH memory segment"); + + __init_load_start__ = ALIGN(__vectors_end__ , 4); + .init ALIGN(__vectors_end__ , 4) : AT(ALIGN(__vectors_end__ , 4)) + { + __init_start__ = .; + *(.init .init.*) + } + __init_end__ = __init_start__ + SIZEOF(.init); + + . = ASSERT(__init_end__ >= __FLASH_segment_start__ && __init_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .init is too large to fit in FLASH memory segment"); + + __text_load_start__ = ALIGN(__init_end__ , 4); + .text ALIGN(__init_end__ , 4) : AT(ALIGN(__init_end__ , 4)) + { + __text_start__ = .; + *(.text .text.* .glue_7t .glue_7 .gnu.linkonce.t.* .gcc_except_table) + } + __text_end__ = __text_start__ + SIZEOF(.text); + + . = ASSERT(__text_end__ >= __FLASH_segment_start__ && __text_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .text is too large to fit in FLASH memory segment"); + + __dtors_load_start__ = ALIGN(__text_end__ , 4); + .dtors ALIGN(__text_end__ , 4) : AT(ALIGN(__text_end__ , 4)) + { + __dtors_start__ = .; + KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) + } + __dtors_end__ = __dtors_start__ + SIZEOF(.dtors); + + . = ASSERT(__dtors_end__ >= __FLASH_segment_start__ && __dtors_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .dtors is too large to fit in FLASH memory segment"); + + __ctors_load_start__ = ALIGN(__dtors_end__ , 4); + .ctors ALIGN(__dtors_end__ , 4) : AT(ALIGN(__dtors_end__ , 4)) + { + __ctors_start__ = .; + KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) + } + __ctors_end__ = __ctors_start__ + SIZEOF(.ctors); + + . = ASSERT(__ctors_end__ >= __FLASH_segment_start__ && __ctors_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .ctors is too large to fit in FLASH memory segment"); + + __rodata_load_start__ = ALIGN(__ctors_end__ , 4); + .rodata ALIGN(__ctors_end__ , 4) : AT(ALIGN(__ctors_end__ , 4)) + { + __rodata_start__ = .; + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + __rodata_end__ = __rodata_start__ + SIZEOF(.rodata); + + . = ASSERT(__rodata_end__ >= __FLASH_segment_start__ && __rodata_end__ <= (__FLASH_segment_start__ + 0x00030000) , "error: .rodata is too large to fit in FLASH memory segment"); + + __fast_load_start__ = ALIGN(__rodata_end__ , 4); + .fast ALIGN(__RAM_segment_start__ , 4) : AT(ALIGN(__rodata_end__ , 4)) + { + __fast_start__ = .; + *(.fast .fast.*) + } + __fast_end__ = __fast_start__ + SIZEOF(.fast); + + __fast_load_end__ = __fast_load_start__ + SIZEOF(.fast); + + . = ASSERT((__fast_load_start__ + SIZEOF(.fast)) >= __FLASH_segment_start__ && (__fast_load_start__ + SIZEOF(.fast)) <= (__FLASH_segment_start__ + 0x00030000) , "error: .fast is too large to fit in FLASH memory segment"); + + .fast_run ALIGN(__RAM_segment_start__ , 4) (NOLOAD) : + { + __fast_run_start__ = .; + . = MAX(__fast_run_start__ + SIZEOF(.fast), .); + } + __fast_run_end__ = __fast_run_start__ + SIZEOF(.fast_run); + + . = ASSERT(__fast_run_end__ >= __RAM_segment_start__ && __fast_run_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .fast_run is too large to fit in RAM memory segment"); + + __data_load_start__ = ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4); + .data ALIGN(__fast_run_end__ , 4) : AT(ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4)) + { + __data_start__ = .; + *(.data .data.* .gnu.linkonce.d.*) + } + __data_end__ = __data_start__ + SIZEOF(.data); + + __data_load_end__ = __data_load_start__ + SIZEOF(.data); + + __FLASH_segment_used_end__ = ALIGN(__fast_load_start__ + SIZEOF(.fast) , 4) + SIZEOF(.data); + + . = ASSERT((__data_load_start__ + SIZEOF(.data)) >= __FLASH_segment_start__ && (__data_load_start__ + SIZEOF(.data)) <= (__FLASH_segment_start__ + 0x00030000) , "error: .data is too large to fit in FLASH memory segment"); + + .data_run ALIGN(__fast_run_end__ , 4) (NOLOAD) : + { + __data_run_start__ = .; + . = MAX(__data_run_start__ + SIZEOF(.data), .); + } + __data_run_end__ = __data_run_start__ + SIZEOF(.data_run); + + . = ASSERT(__data_run_end__ >= __RAM_segment_start__ && __data_run_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .data_run is too large to fit in RAM memory segment"); + + __bss_load_start__ = ALIGN(__data_run_end__ , 4); + .bss ALIGN(__data_run_end__ , 4) (NOLOAD) : AT(ALIGN(__data_run_end__ , 4)) + { + __bss_start__ = .; + *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) + } + __bss_end__ = __bss_start__ + SIZEOF(.bss); + + . = ASSERT(__bss_end__ >= __RAM_segment_start__ && __bss_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .bss is too large to fit in RAM memory segment"); + + __non_init_load_start__ = ALIGN(__bss_end__ , 4); + .non_init ALIGN(__bss_end__ , 4) (NOLOAD) : AT(ALIGN(__bss_end__ , 4)) + { + __non_init_start__ = .; + *(.non_init .non_init.*) + } + __non_init_end__ = __non_init_start__ + SIZEOF(.non_init); + + . = ASSERT(__non_init_end__ >= __RAM_segment_start__ && __non_init_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .non_init is too large to fit in RAM memory segment"); + + __heap_load_start__ = ALIGN(__non_init_end__ , 4); + .heap ALIGN(__non_init_end__ , 4) (NOLOAD) : AT(ALIGN(__non_init_end__ , 4)) + { + __heap_start__ = .; + *(.heap) + . = ALIGN(MAX(__heap_start__ + __HEAPSIZE__ , .), 4); + } + __heap_end__ = __heap_start__ + SIZEOF(.heap); + + . = ASSERT(__heap_end__ >= __RAM_segment_start__ && __heap_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .heap is too large to fit in RAM memory segment"); + + __stack_load_start__ = ALIGN(__heap_end__ , 4); + .stack ALIGN(__heap_end__ , 4) (NOLOAD) : AT(ALIGN(__heap_end__ , 4)) + { + __stack_start__ = .; + *(.stack) + . = ALIGN(MAX(__stack_start__ + __STACKSIZE__ , .), 4); + } + __stack_end__ = __stack_start__ + SIZEOF(.stack); + + . = ASSERT(__stack_end__ >= __RAM_segment_start__ && __stack_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .stack is too large to fit in RAM memory segment"); + + __stack_process_load_start__ = ALIGN(__stack_end__ , 4); + .stack_process ALIGN(__stack_end__ , 4) (NOLOAD) : AT(ALIGN(__stack_end__ , 4)) + { + __stack_process_start__ = .; + *(.stack_process) + . = ALIGN(MAX(__stack_process_start__ + __STACKSIZE_PROCESS__ , .), 4); + } + __stack_process_end__ = __stack_process_start__ + SIZEOF(.stack_process); + + __RAM_segment_used_end__ = ALIGN(__stack_end__ , 4) + SIZEOF(.stack_process); + + . = ASSERT(__stack_process_end__ >= __RAM_segment_start__ && __stack_process_end__ <= (__RAM_segment_start__ + 0x00020000) , "error: .stack_process is too large to fit in RAM memory segment"); + +} + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c new file mode 100644 index 00000000..283dc71a --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.c @@ -0,0 +1,127 @@ +/* Small demonstration of the ThreadX module manager. */ + +#include "tx_api.h" +#include "txm_module.h" + + +#define DEMO_STACK_SIZE 1024 + +/* Define the ThreadX object control blocks... */ + +TX_THREAD module_manager; +TXM_MODULE_INSTANCE my_module; + + +/* Define the object pool area. */ + +UCHAR object_memory[16384]; + + +/* Define the module data pool area. */ + +#define MODULE_DATA_SIZE (256 * 1024) +#define MODULE_DATA (0x40000) + + +/* The module code should be loaded here. */ + +#define MODULE_CODE (0x30000) +//#define MODULE_CODE (0x309c0) + + +/* Define the external memory area. */ + +#define EXTERNAL_MEMORY_SIZE (64 * 1024) +#define EXTERNAL_MEMORY (0x80000) + + +/* Define the count of memory faults. */ + +ULONG memory_faults; + + +/* Define thread prototypes. */ + +void module_manager_entry(ULONG thread_input); + + +/* Define fault handler. */ + +VOID module_fault_handler(TX_THREAD *thread, TXM_MODULE_INSTANCE *module) +{ + + /* Just increment the fault counter. */ + memory_faults++; +} + +/* 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 = (CHAR*)first_unused_memory; + + + tx_thread_create(&module_manager, "Module Manager Thread", module_manager_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; +} + + + + +/* Define the test threads. */ + +void module_manager_entry(ULONG thread_input) +{ + + /* Initialize the module manager. */ + txm_module_manager_initialize((void *) MODULE_DATA, MODULE_DATA_SIZE); + + txm_module_manager_object_pool_create(object_memory, sizeof(object_memory)); + + /* Register a fault handler. */ + txm_module_manager_memory_fault_notify(module_fault_handler); + + /* Load the module that is already there, in this example it is placed there by the multiple image download. */ + txm_module_manager_in_place_load(&my_module, "my module", (void *) MODULE_CODE); + + /* Enable a read/write shared memory region. */ + txm_module_manager_external_memory_enable(&my_module, (void *) EXTERNAL_MEMORY, EXTERNAL_MEMORY_SIZE, TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE); + + /* Start the module. */ + txm_module_manager_start(&my_module); + + /* Sleep for a while.... */ + tx_thread_sleep(1000); + + /* Stop the module. */ + txm_module_manager_stop(&my_module); + + /* Unload the module. */ + txm_module_manager_unload(&my_module); + + /* Load the module that is already there. */ + txm_module_manager_in_place_load(&my_module, "my module", (void *) MODULE_CODE); + + /* Start the module again. */ + txm_module_manager_start(&my_module); + + /* Now just spin... */ + while(1) + { + + tx_thread_sleep(100); + } +} diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch new file mode 100644 index 00000000..debe6d72 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/sample_threadx_module_manager.launch @@ -0,0 +1,376 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_initialize_low_level.S b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_initialize_low_level.S new file mode 100644 index 00000000..fd032178 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_initialize_low_level.S @@ -0,0 +1,233 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global __RAM_segment_used_end__ + .global _tx_timer_interrupt + .global __main + .global _vectors + .global __tx_NMIHandler // NMI + .global __tx_BadHandler // HardFault + .global __tx_SVCallHandler // SVCall + .global __tx_DBGHandler // Monitor + .global __tx_PendSVHandler // PendSV + .global __tx_SysTickHandler // SysTick + .global __tx_IntHandler // Int 0 + + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M0+/GNU */ +/* 6.1.10 */ +/* 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .thumb_func +_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 + ADDS r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + + LDR r0, =0xE000ED08 // Build address of NVIC registers + LDR r1, =_vectors // Pickup address of vector table + STR r1, [r0] // Set vector table address + + /* 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 + + /* Enable the cycle count register. */ + + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + MOVS r2, #1 + ORRS r1, r1, r2 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ + + LDR r0, =0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + MOVS r2, #0x14 + STR r1, [r0, r2] // Setup SysTick Reload Value + MOVS r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + LDR r2, =0xD18 + STR r1, [r0, r2] // Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + LDR r2, =0xD1C + STR r1, [r0, r2] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + LDR r2, =0xD20 + STR r1, [r0, r2] // 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. */ + + .global __tx_BadHandler + .thumb_func +__tx_BadHandler: + B __tx_BadHandler + +/* Generic interrupt handler template */ + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + + /* Do interrupt handler work here */ + /* BL .... */ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + +/* System Tick timer interrupt handler */ + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +// } + + +/* NMI, DBG handlers */ + .global __tx_NMIHandler + .thumb_func +__tx_NMIHandler: + B __tx_NMIHandler + + .global __tx_DBGHandler + .thumb_func +__tx_DBGHandler: + B __tx_DBGHandler + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_enter + .thumb_func +_tx_execution_isr_enter: + BX LR +#endif + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +/* Execution change notify functions */ + .global _tx_execution_isr_exit + .thumb_func +_tx_execution_isr_exit: + BX LR +#endif diff --git a/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_simulator_startup.s b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_simulator_startup.s new file mode 100644 index 00000000..60662fe3 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/sample_threadx_module_manager/tx_simulator_startup.s @@ -0,0 +1,73 @@ + + .syntax unified + .section .vectors, "ax" + .code 16 + .align 0 + .global _vectors + +_vectors: + .word __stack_end__ + .word reset_handler + .word __tx_NMIHandler + .word HardFaultException + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word __tx_SVCallHandler //_SVC_Handler - used by Threadx scheduler // + .word __tx_DBGHandler + .word 0 // Reserved + .word __tx_PendSVHandler + .word __tx_SysTickHandler // Used by Threadx timer functionality + .word __tx_BadHandler // Populate with user Interrupt handler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + + + + .section .init, "ax" + .global reset_handler + .thumb_func +reset_handler: + +// low level hardware config, such as PLL setup goes here + + b _start + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/tx/.cproject b/ports_module/cortex_m0+/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..a1c0d7ab --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/tx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/tx/.project b/ports_module/cortex_m0+/gnu/example_build/tx/.project new file mode 100644 index 00000000..207f353b --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/tx/.project @@ -0,0 +1,63 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_generic_module_manager + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_manager/inc + + + inc_generic_modules + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/src + + + src_generic_module_manager + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_manager/src + + + src_port_module_manager + 2 + $%7BPARENT-2-PROJECT_LOC%7D/module_manager/src + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/txm/.cproject b/ports_module/cortex_m0+/gnu/example_build/txm/.cproject new file mode 100644 index 00000000..f04340b4 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/txm/.cproject @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_module/cortex_m0+/gnu/example_build/txm/.project b/ports_module/cortex_m0+/gnu/example_build/txm/.project new file mode 100644 index 00000000..8b510516 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/example_build/txm/.project @@ -0,0 +1,58 @@ + + + txm + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common/inc + + + inc_generic_module_manager + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_manager/inc + + + inc_generic_modules + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic_module_lib + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_modules/module_lib/src + + + src_port_module_lib + 2 + $%7BPARENT-2-PROJECT_LOC%7D/module_lib/src + + + diff --git a/ports_module/cortex_m0+/gnu/inc/tx_port.h b/ports_module/cortex_m0+/gnu/inc/tx_port.h new file mode 100644 index 00000000..06089b0f --- /dev/null +++ b/ports_module/cortex_m0+/gnu/inc/tx_port.h @@ -0,0 +1,537 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-M0+/GNU */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; + + +/* Define the TX_MEMSET macro to remove library reference. */ + +#define TX_MEMSET(a,b,c) { \ + UCHAR *ptr; \ + UCHAR value; \ + UINT i, size; \ + ptr = (UCHAR *) ((VOID *) a); \ + value = (UCHAR) b; \ + size = (UINT) c; \ + for (i = 0; i < size; i++) \ + { \ + *ptr++ = value; \ + } \ + } + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX Cortex-M0+ port. */ + +#define TX_INT_DISABLE 1 /* Disable interrupts */ +#define TX_INT_ENABLE 0 /* Enable interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0xE0001004) +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (0) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#define TX_INLINE_INITIALIZATION + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; +#define TX_THREAD_EXTENSION_3 + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION VOID *tx_event_flags_group_module_instance; \ + VOID (*tx_event_flags_group_set_module_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *group_ptr); + +#define TX_QUEUE_EXTENSION VOID *tx_queue_module_instance; \ + VOID (*tx_queue_send_module_notify)(struct TX_QUEUE_STRUCT *queue_ptr); + +#define TX_SEMAPHORE_EXTENSION VOID *tx_semaphore_module_instance; \ + VOID (*tx_semaphore_put_module_notify)(struct TX_SEMAPHORE_STRUCT *semaphore_ptr); + +#define TX_TIMER_EXTENSION VOID *tx_timer_module_instance; \ + VOID (*tx_timer_module_expiration_function)(ULONG id); + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) + +#ifdef TX_ENABLE_FPU_SUPPORT + + +#ifdef TX_MISRA_ENABLE + +ULONG _tx_misra_control_get(void); +void _tx_misra_control_set(ULONG value); +ULONG _tx_misra_fpccr_get(void); +void _tx_misra_vfp_touch(void); + +#else + +__attribute__( ( always_inline ) ) static inline ULONG __get_control(void) +{ + +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); +} + + +__attribute__( ( always_inline ) ) static inline void __set_control(ULONG control_value) +{ + + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); +} + + +#endif + + +/* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA + in order to ensure no lazy stacking will occur. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } + +#endif + + +/* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. + If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + the lazy FPU save, then restore the CONTROL.FPCA state. */ + +#ifndef TX_MISRA_ENABLE + + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + __asm__ volatile ("vmov.f32 s0, s0"); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } \ + } \ + } \ + } +#else + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } +#endif + +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +#endif + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define the get system state macro. */ + +#ifndef TX_THREAD_GET_SYSTEM_STATE +#ifndef TX_MISRA_ENABLE + +__attribute__( ( always_inline ) ) static inline unsigned int __get_ipsr_value(void) +{ + +unsigned int ipsr_value; + + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} + + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_ipsr_value()) +#else +ULONG _tx_misra_ipsr_get(VOID); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) +#endif +#endif + + +/* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value + indicates that _tx_thread_system_return should not be called. */ + +#ifndef TX_THREAD_SYSTEM_RETURN_CHECK +#define TX_THREAD_SYSTEM_RETURN_CHECK(c) (c) = ((ULONG) _tx_thread_preempt_disable); +#endif + + +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to + prevent early scheduling on Cortex-M parts. */ + +#define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; + + +/* This ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) { (b) = 0; \ + (m) = (m) & (-(m)); \ + while ((m) >>= 1) \ + { \ + (b)++; \ + } \ + } + + +#endif + + +#ifndef TX_DISABLE_INLINE + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ + +unsigned int primask_value; + + __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); + __asm__ volatile (" CPSID i" : : : "memory" ); + return(primask_value); +} + +__attribute__( ( always_inline ) ) static inline void __restore_interrupts(unsigned int primask_value) +{ + + __asm__ volatile (" MSR PRIMASK,%0": : "r" (primask_value): "memory" ); +} + +__attribute__( ( always_inline ) ) static inline unsigned int __get_primask_value(void) +{ + +unsigned int primask_value; + + __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); + return(primask_value); +} + +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + + __asm__ volatile (" CPSIE i": : : "memory" ); +} + + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +unsigned int interrupt_save; + + *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (__get_ipsr_value() == 0) + { + interrupt_save = __get_primask_value(); + __enable_interrupts(); + __restore_interrupts(interrupt_save); + } +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupts(interrupt_save); + + +/* Redefine _tx_thread_system_return for improved performance. */ + +#define _tx_thread_system_return _tx_thread_system_return_inline + + +#else + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); +#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); +#endif + + +/* Define FPU extension for the Cortex-M7. Each is assumed to be called in the context of the executing + thread. These are no longer needed, but are preserved for backward compatibility only. */ + +void tx_thread_fpu_enable(void); +void tx_thread_fpu_disable(void); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M0+/AC6 Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_module/cortex_m0+/gnu/inc/txm_module_port.h b/ports_module/cortex_m0+/gnu/inc/txm_module_port.h new file mode 100644 index 00000000..54956a1e --- /dev/null +++ b/ports_module/cortex_m0+/gnu/inc/txm_module_port.h @@ -0,0 +1,381 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* txm_module_port.h Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the basic module constants, interface structures, */ +/* and function prototypes. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TXM_MODULE_PORT_H +#define TXM_MODULE_PORT_H + +/* Determine if the optional Modules user define file should be used. */ + +#ifdef TXM_MODULE_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in txm_module_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "txm_module_user.h" +#endif + +/* It is assumed that the base ThreadX tx_port.h file has been modified to add the + following extensions to the ThreadX thread control block (this code should replace + the corresponding macro define in tx_port.h): + +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; + +The following extensions must also be defined in tx_port.h: + +#define TX_EVENT_FLAGS_GROUP_EXTENSION VOID *tx_event_flags_group_module_instance; \ + VOID (*tx_event_flags_group_set_module_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *group_ptr); + +#define TX_QUEUE_EXTENSION VOID *tx_queue_module_instance; \ + VOID (*tx_queue_send_module_notify)(struct TX_QUEUE_STRUCT *queue_ptr); + +#define TX_SEMAPHORE_EXTENSION VOID *tx_semaphore_module_instance; \ + VOID (*tx_semaphore_put_module_notify)(struct TX_SEMAPHORE_STRUCT *semaphore_ptr); + +#define TX_TIMER_EXTENSION VOID *tx_timer_module_instance; \ + VOID (*tx_timer_module_expiration_function)(ULONG id); +*/ + +/* Define the kernel stack size for a module thread. */ +#ifndef TXM_MODULE_KERNEL_STACK_SIZE +#define TXM_MODULE_KERNEL_STACK_SIZE 768 +#endif + +/* For the following 3 access control settings, change TEX and C, B, S (bits 21 through 16 of MPU_RASR) + * to reflect your system memory attributes (cache, shareable, memory type). */ +/* Code region access control: privileged read-only, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_CODE_ACCESS_CONTROL +#define TXM_MODULE_MPU_CODE_ACCESS_CONTROL 0x06070000 +#endif +/* Data region access control: execute never, read/write, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_DATA_ACCESS_CONTROL +#define TXM_MODULE_MPU_DATA_ACCESS_CONTROL 0x13070000 +#endif +/* Shared region access control: execute never, read-only, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_SHARED_ACCESS_CONTROL +#define TXM_MODULE_MPU_SHARED_ACCESS_CONTROL 0x12070000 +#endif + +/* Define constants specific to the tools the module can be built with for this particular modules port. */ + +#define TXM_MODULE_IAR_COMPILER 0x00000000 +#define TXM_MODULE_RVDS_COMPILER 0x01000000 +#define TXM_MODULE_GNU_COMPILER 0x02000000 +#define TXM_MODULE_COMPILER_MASK 0xFF000000 +#define TXM_MODULE_OPTIONS_MASK 0x000000FF + + +/* Define the properties for this particular module port. */ + +#define TXM_MODULE_MEMORY_PROTECTION_ENABLED + +#ifdef TXM_MODULE_MEMORY_PROTECTION_ENABLED +#define TXM_MODULE_REQUIRE_ALLOCATED_OBJECT_MEMORY +#else +#define TXM_MODULE_REQUIRE_LOCAL_OBJECT_MEMORY +#endif + +#define TXM_MODULE_USER_MODE 0x00000001 +#define TXM_MODULE_MEMORY_PROTECTION 0x00000002 +#define TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS 0x00000004 + + +/* Define the supported options for this module. */ + +#define TXM_MODULE_MANAGER_SUPPORTED_OPTIONS (TXM_MODULE_USER_MODE | TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS) +#define TXM_MODULE_MANAGER_REQUIRED_OPTIONS 0 + + +/* Define offset adjustments according to the compiler used to build the module. */ + +#define TXM_MODULE_IAR_SHELL_ADJUST 24 +#define TXM_MODULE_IAR_START_ADJUST 28 +#define TXM_MODULE_IAR_STOP_ADJUST 32 +#define TXM_MODULE_IAR_CALLBACK_ADJUST 44 + +#define TXM_MODULE_RVDS_SHELL_ADJUST 0 +#define TXM_MODULE_RVDS_START_ADJUST 0 +#define TXM_MODULE_RVDS_STOP_ADJUST 0 +#define TXM_MODULE_RVDS_CALLBACK_ADJUST 0 + +#define TXM_MODULE_GNU_SHELL_ADJUST 24 +#define TXM_MODULE_GNU_START_ADJUST 28 +#define TXM_MODULE_GNU_STOP_ADJUST 32 +#define TXM_MODULE_GNU_CALLBACK_ADJUST 44 + + +/* Define other module port-specific constants. */ + +/* Define INLINE_DECLARE to inline for ARM compiler. */ + +#define INLINE_DECLARE inline + +#ifdef TXM_MODULE_MANAGER_16_MPU + +/* Define the number of MPU entries assigned to the code and data sections. + On some Cortex-M7 parts, there are 16 total entries. ThreadX uses one for access + to the kernel entry function, thus 15 remain for code and data protection. */ +#define TXM_MODULE_MPU_TOTAL_ENTRIES 16 +#define TXM_MODULE_MPU_CODE_ENTRIES 4 +#define TXM_MODULE_MPU_DATA_ENTRIES 4 +#define TXM_MODULE_MPU_SHARED_ENTRIES 3 + +#define TXM_MODULE_MPU_KERNEL_ENTRY_INDEX 0 +#define TXM_MODULE_MPU_SHARED_INDEX 9 + +#define TXM_ENABLE_REGION 0x01 + +/* There are 2 registers to set up each MPU region: MPU_RBAR, MPU_RASR. */ +typedef struct TXM_MODULE_MPU_INFO_STRUCT +{ + ULONG txm_module_mpu_region_address; + ULONG txm_module_mpu_region_attribute_size; +} TXM_MODULE_MPU_INFO; +/* Shared memory region attributes. */ +#define TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE 1 +#define TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT 0x01000000 + +/* Define the port-extensions to the module manager instance structure. */ + +#define TXM_MODULE_MANAGER_PORT_EXTENSION \ + TXM_MODULE_MPU_INFO txm_module_instance_mpu_registers[TXM_MODULE_MPU_TOTAL_ENTRIES]; \ + ULONG txm_module_instance_shared_memory_count; \ + ULONG txm_module_instance_shared_memory_address[TXM_MODULE_MPU_SHARED_ENTRIES]; \ + ULONG txm_module_instance_shared_memory_length[TXM_MODULE_MPU_SHARED_ENTRIES]; + +#else /* TXM_MODULE_MANAGER_16_MPU is not defined */ + +/* Define the number of MPU entries assigned to the code and data sections. + On Cortex-M0/M0+, M4, and some M7 parts, there are 8 total entries. ThreadX uses one for access + to the kernel entry function, thus 7 remain for code and data protection. */ +#define TXM_MODULE_MANAGER_CODE_MPU_ENTRIES 4 +#define TXM_MODULE_MANAGER_DATA_MPU_ENTRIES 3 +#define TXM_MODULE_MANAGER_SHARED_MPU_INDEX 8 +#define TXM_MODULE_MANAGER_SHARED_MPU_REGION 4 + +/* Shared memory region attributes. */ +#define TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE 1 +#define TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT 0x01000000 + +/* Define the port-extensions to the module manager instance structure. */ + +#define TXM_MODULE_MANAGER_PORT_EXTENSION \ + ULONG txm_module_instance_mpu_registers[16]; \ + ULONG txm_module_instance_shared_memory_address; \ + ULONG txm_module_instance_shared_memory_length; + +#endif /* TXM_MODULE_MANAGER_16_MPU */ + +/* Define the memory fault information structure that is populated when a memory fault occurs. */ + + +typedef struct TXM_MODULE_MANAGER_MEMORY_FAULT_INFO_STRUCT +{ + TX_THREAD *txm_module_manager_memory_fault_info_thread_ptr; + VOID *txm_module_manager_memory_fault_info_code_location; + ULONG txm_module_manager_memory_fault_info_shcsr; + ULONG txm_module_manager_memory_fault_info_cfsr; + ULONG txm_module_manager_memory_fault_info_mmfar; + ULONG txm_module_manager_memory_fault_info_bfar; + ULONG txm_module_manager_memory_fault_info_control; + ULONG txm_module_manager_memory_fault_info_sp; + ULONG txm_module_manager_memory_fault_info_r0; + ULONG txm_module_manager_memory_fault_info_r1; + ULONG txm_module_manager_memory_fault_info_r2; + ULONG txm_module_manager_memory_fault_info_r3; + ULONG txm_module_manager_memory_fault_info_r4; + ULONG txm_module_manager_memory_fault_info_r5; + ULONG txm_module_manager_memory_fault_info_r6; + ULONG txm_module_manager_memory_fault_info_r7; + ULONG txm_module_manager_memory_fault_info_r8; + ULONG txm_module_manager_memory_fault_info_r9; + ULONG txm_module_manager_memory_fault_info_r10; + ULONG txm_module_manager_memory_fault_info_r11; + ULONG txm_module_manager_memory_fault_info_r12; + ULONG txm_module_manager_memory_fault_info_lr; + ULONG txm_module_manager_memory_fault_info_xpsr; +} TXM_MODULE_MANAGER_MEMORY_FAULT_INFO; + + +#define TXM_MODULE_MANAGER_FAULT_INFO \ + TXM_MODULE_MANAGER_MEMORY_FAULT_INFO _txm_module_manager_memory_fault_info; + +/* Define the macro to check the code alignment. */ + +#define TXM_MODULE_MANAGER_CHECK_CODE_ALIGNMENT(module_location, code_alignment) \ + { \ + ULONG temp; \ + temp = (ULONG) module_location; \ + temp = temp & (code_alignment - 1); \ + if (temp) \ + { \ + _tx_mutex_put(&_txm_module_manager_mutex); \ + return(TXM_MODULE_ALIGNMENT_ERROR); \ + } \ + } + + +/* Define the macro to adjust the alignment and size for code/data areas. */ + +#define TXM_MODULE_MANAGER_ALIGNMENT_ADJUST(module_preamble, code_size, code_alignment, data_size, data_alignment) _txm_module_manager_alignment_adjust(module_preamble, &code_size, &code_alignment, &data_size, &data_alignment); + + +/* Define the macro to adjust the symbols in the module preamble. */ + +#define TXM_MODULE_MANAGER_CALCULATE_ADJUSTMENTS(properties, shell_function_adjust, start_function_adjust, stop_function_adjust, callback_function_adjust) \ + if ((properties & TXM_MODULE_COMPILER_MASK) == TXM_MODULE_IAR_COMPILER) \ + { \ + shell_function_adjust = TXM_MODULE_IAR_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_IAR_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_IAR_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_IAR_CALLBACK_ADJUST; \ + } \ + else if ((properties & TXM_MODULE_COMPILER_MASK) == TXM_MODULE_RVDS_COMPILER) \ + { \ + shell_function_adjust = TXM_MODULE_RVDS_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_RVDS_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_RVDS_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_RVDS_CALLBACK_ADJUST; \ + } \ + else \ + { \ + shell_function_adjust = TXM_MODULE_GNU_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_GNU_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_GNU_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_GNU_CALLBACK_ADJUST; \ + } + + +/* Define the macro to populate the thread control block with module port-specific information. + Check if the module is in user mode and set up txm_module_thread_entry_info_kernel_call_dispatcher accordingly. +*/ + +#define TXM_MODULE_MANAGER_THREAD_SETUP(thread_ptr, module_instance) \ + thread_ptr -> tx_thread_module_current_user_mode = module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE; \ + thread_ptr -> tx_thread_module_user_mode = module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE; \ + if (thread_ptr -> tx_thread_module_user_mode) \ + { \ + thread_entry_info -> txm_module_thread_entry_info_kernel_call_dispatcher = _txm_module_manager_user_mode_entry; \ + } \ + else \ + { \ + thread_entry_info -> txm_module_thread_entry_info_kernel_call_dispatcher = _txm_module_manager_kernel_dispatch; \ + } + + +/* Define the macro to populate the module control block with module port-specific information. + If memory protection is enabled, set up the MPU registers. +*/ +#define TXM_MODULE_MANAGER_MODULE_SETUP(module_instance) \ + if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE) \ + { \ + if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) \ + { \ + _txm_module_manager_mm_register_setup(module_instance); \ + } \ + } \ + else \ + { \ + /* Do nothing. */ \ + } + +/* Define the macro to perform port-specific functions when unloading the module. */ +/* Nothing needs to be done for this port. */ +#define TXM_MODULE_MANAGER_MODULE_UNLOAD(module_instance) + + +/* Define the macros to perform port-specific checks when passing pointers to the kernel. */ + +/* Define macro to make sure object is inside the module's data. */ +#ifdef TXM_MODULE_MANAGER_16_MPU +#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \ + _txm_module_manager_inside_data_check(module_instance, obj_ptr, obj_size) +#else +#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \ + /* Check for overflow. */ \ + (((obj_ptr) < ((obj_ptr) + (obj_size))) && \ + /* Check if it's inside module data. */ \ + ((((obj_ptr) >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && \ + (((obj_ptr) + (obj_size)) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1))) || \ + /* Check if it's inside shared memory. */ \ + (((obj_ptr) >= (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address) && \ + (((obj_ptr) + (obj_size)) <= (ALIGN_TYPE) (module_instance -> txm_module_instance_shared_memory_address + module_instance -> txm_module_instance_shared_memory_length))))) +#endif + +/* Define some internal prototypes to this module port. */ + +#ifndef TX_SOURCE_CODE +#define txm_module_manager_memory_fault_notify _txm_module_manager_memory_fault_notify +#endif + + +#define TXM_MODULE_MANAGER_ADDITIONAL_PROTOTYPES \ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size, ULONG *code_alignment, ULONG *data_size, ULONG *data_alignment); \ +VOID _txm_module_manager_memory_fault_handler(VOID); \ +UINT _txm_module_manager_memory_fault_notify(VOID (*notify_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)); \ +VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance); \ +ULONG _txm_power_of_two_block_size(ULONG size); \ +ULONG _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length); \ +ULONG _txm_module_manager_region_size_get(ULONG block_size); \ +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size); + +#define TXM_MODULE_MANAGER_VERSION_ID \ +CHAR _txm_module_manager_version_id[] = \ + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M0+/GNU Version 6.1.10 *"; + +#endif diff --git a/ports_module/cortex_m0+/gnu/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m0+/gnu/module_lib/src/txm_module_thread_shell_entry.c new file mode 100644 index 00000000..4da27152 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_lib/src/txm_module_thread_shell_entry.c @@ -0,0 +1,173 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef TXM_MODULE +#define TXM_MODULE +#endif + +#ifndef TX_SOURCE_CODE +#define TX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "txm_module.h" +#include "tx_thread.h" + +/* Define the global module entry pointer from the start thread of the module. */ + +TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; + + +/* Define the dispatch function pointer used in the module implementation. */ + +ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param_3); + + +/* Define the startup code that clears the uninitialized global data and sets up the + preset global variables. */ + +extern VOID _gcc_setup(TXM_MODULE_INSTANCE *); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_thread_shell_entry Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the specified entry function of the thread. It */ +/* also provides a place for the thread's entry function to return. */ +/* If the thread returns, this function places the thread in a */ +/* "COMPLETED" state. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to current thread */ +/* thread_info Pointer to thread entry info */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_initialize cstartup initialization */ +/* thread_entry Thread's entry function */ +/* tx_thread_resume Resume the module callback thread */ +/* _txm_module_thread_system_suspend Module thread suspension routine */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) +{ + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + VOID (*entry_exit_notify)(TX_THREAD *, UINT); +#endif + + + /* Determine if this is the start thread. If so, we must prepare the module for + execution. If not, simply skip the C startup code. */ + if (thread_info -> txm_module_thread_entry_info_start_thread) + { + /* Initialize the C environment. */ + _gcc_setup(thread_info -> txm_module_thread_entry_info_code_base_address); + + /* Save the entry info pointer, for later use. */ + _txm_module_entry_info = thread_info; + + /* Save the kernel function dispatch address. This is used to make all resident calls from + the module. */ + _txm_module_kernel_call_dispatcher = thread_info -> txm_module_thread_entry_info_kernel_call_dispatcher; + + /* Ensure that we have a valid pointer. */ + while (!_txm_module_kernel_call_dispatcher) + { + /* Loop here, if an error is present getting the dispatch function pointer! + An error here typically indicates the resident portion of _tx_thread_schedule + is not supporting the trap to obtain the function pointer. */ + } + + /* Resume the module's callback thread, already created in the manager. */ + _txe_thread_resume(thread_info -> txm_module_thread_entry_info_callback_request_thread); + } + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + + /* Pickup the entry/exit application callback routine. */ + entry_exit_notify = thread_info -> txm_module_thread_entry_info_exit_notify; + + /* Determine if an application callback routine is specified. */ + if (entry_exit_notify != TX_NULL) + { + + /* Yes, notify application that this thread has been entered! */ + (entry_exit_notify)(thread_ptr, TX_THREAD_ENTRY); + } +#endif + + /* Call current thread's entry function. */ + (thread_info -> txm_module_thread_entry_info_entry) (thread_info -> txm_module_thread_entry_info_parameter); + + /* Suspend thread with a "completed" state. */ + + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + + /* Pickup the entry/exit application callback routine again. */ + entry_exit_notify = thread_info -> txm_module_thread_entry_info_exit_notify; + + /* Determine if an application callback routine is specified. */ + if (entry_exit_notify != TX_NULL) + { + + /* Yes, notify application that this thread has exited! */ + (entry_exit_notify)(thread_ptr, TX_THREAD_EXIT); + } +#endif + + /* Call actual thread suspension routine. */ + _txm_module_thread_system_suspend(thread_ptr); + +#ifdef TX_SAFETY_CRITICAL + + /* If we ever get here, raise safety critical exception. */ + TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); +#endif +} diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_restore.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_restore.S new file mode 100644 index 00000000..b24f90e5 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_restore.S @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_isr_exit +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_exit] Execution profiling ISR exit */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .thumb_func +_tx_thread_context_restore: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR exit function to indicate an ISR is complete. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_exit // Call the ISR exit function + POP {r0, r1} // Recover return address + MOV lr, r1 +#endif + + BX lr +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_save.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_save.S new file mode 100644 index 00000000..9ea5e58c --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_context_save.S @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_isr_enter +#endif +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_enter] Execution profiling ISR enter */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .thumb_func +_tx_thread_context_save: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR enter function to indicate an ISR is starting. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_enter // Call the ISR enter function + POP {r0, r1} // Recover return address + MOV lr, r1 +#endif + + /* Context is already saved - just return. */ + + BX lr +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_control.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..894d7a6a --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .thumb_func +_tx_thread_interrupt_control: + +/* Pickup current interrupt lockout posture. */ + + MRS r1, PRIMASK // Pickup current interrupt lockout + + +/* Apply the new interrupt posture. */ + + MSR PRIMASK, r0 // Apply the new interrupt lockout + MOV r0, r1 // Transfer old to return register + BX lr // Return to caller + +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_disable.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..15ae1027 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_disable.S @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts and returning */ +/* the previous interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(VOID) +// { + .global _tx_thread_interrupt_disable + .thumb_func +_tx_thread_interrupt_disable: + +/* Pickup current interrupt lockout posture. */ + + MRS r0, PRIMASK + CPSID i + BX lr + +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_restore.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..b66702ef --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_interrupt_restore.S @@ -0,0 +1,72 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring the previous */ +/* interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* previous_posture Previous interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_interrupt_restore(UINT previous_posture) +// { + .global _tx_thread_interrupt_restore + .thumb_func +_tx_thread_interrupt_restore: + /* Restore previous interrupt lockout posture. */ + MSR PRIMASK, r0 + BX lr +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_schedule.S new file mode 100644 index 00000000..42c3e8d9 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_schedule.S @@ -0,0 +1,564 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_thread_enter + .global _tx_execution_thread_exit +#endif +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global _tx_thread_schedule + .thumb_func +.type _tx_thread_schedule, function +_tx_thread_schedule: + /* This function should only ever be called on Cortex-M + from the first schedule request. Subsequent scheduling occurs + from the PendSV handling routine below. */ + + /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets. */ + MOVS r0, #0 // Build value for TX_FALSE + LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag + STR r0, [r2, #0] // Clear preempt disable flag + + /* Enable interrupts */ + CPSIE i + + /* Enter the scheduler for the first time. */ + LDR r0, =0x10000000 // Load PENDSVSET bit + LDR r1, =0xE000ED04 // Load ICSR address + STR r0, [r1] // Set PENDSVBIT in ICSR + DSB // Complete all memory accesses + ISB // Flush pipeline + + /* Wait here for the PendSV to take place. */ + +__tx_wait_here: + B __tx_wait_here // Wait for the PendSV to happen +// } + + + /* Memory Exception Handler. */ + .global HardFaultException + .thumb_func +HardFaultException: + .global MemManage_Handler + .global BusFault_Handler + .global UsageFault_Handler + .thumb_func +MemManage_Handler: + .thumb_func +BusFault_Handler: + .thumb_func +UsageFault_Handler: + + CPSID i // Disable interrupts + + /* Now pickup and store all the fault related information. */ + + LDR r2,=_txm_module_manager_memory_fault_info // Pickup fault info struct + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + STR r1, [r2, #0] // Save current thread pointer in fault info structure + MRS r0, CONTROL // Pickup current CONTROL register + STR r0, [r2, #24] // Save CONTROL + MRS r1, PSP // Pickup thread stack pointer + STR r1, [r2, #28] // Save thread stack pointer + LDR r0, [r1] // Pickup saved r0 (as r0-r3, r12, lr, pc, xpsr are automatically saved to stack) + STR r0, [r2, #32] // Save r0 + LDR r0, [r1, #4] // Pickup saved r1 + STR r0, [r2, #36] // Save r1 + LDR r0, [r1, #8] // Pickup saved r2 + STR r0, [r2, #40] // Save r2 + STR r3, [r2, #44] // Save r3 + STR r4, [r2, #48] // Save r4 + STR r5, [r2, #52] // Save r5 + STR r6, [r2, #56] // Save r6 + STR r7, [r2, #60] // Save r7 + MOV r0, r8 // Pickup current r8 register (high register) + STR r0, [r2, #64] // Save r8 + MOV r0, r9 // Pickup current r9 register + STR r0, [r2, #68] // Save r9 + MOV r0, r10 // Pickup current r10 register + STR r0, [r2, #72] // Save r10 + MOV r0, r11 // Pickup current r11 register + STR r0, [r2, #76] // Save r11 + LDR r0, [r1, #16] // Pickup saved r12 + STR r0, [r2, #80] // Save r12 + LDR r0, [r1, #20] // Pickup saved lr + STR r0, [r2, #84] // Save lr + LDR r0, [r1, #24] // Pickup instruction address at point of fault + STR r0, [r2, #4] // Save point of fault + LDR r0, [r1, #28] // Pickup xPSR + STR r0, [r2, #88] // Save xPSR + + MRS r0, CONTROL // Pickup current CONTROL register + MOVS r1, #1 + BICS r0, r0, r1 // Clear the UNPRIV bit + MSR CONTROL, r0 // Setup new CONTROL register + + BL _txm_module_manager_memory_fault_handler // Call memory manager fault handler + +#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. */ + CPSID i // Disable interrupts + BL _tx_execution_thread_exit // Call the thread exit function + CPSIE i // Enable interrupts +#endif + + MOVS 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 MemManage_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 + LDR r0, =0xFFFFFFFD // Exception return + MOV lr, r0 // Move exception return to lr + BX lr // Return from exception + + + /* Generic context switching PendSV handler. */ + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global PendSV_Handler + .global __tx_PendSVHandler + .syntax unified + .thumb_func +PendSV_Handler: + .thumb_func +__tx_PendSVHandler: + + /* Get current thread value and new thread pointer. */ + +__tx_ts_handler: + +#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. */ + CPSID i // Disable interrupts + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, r1} // Recover LR + MOV lr, r1 // + CPSIE i // Enable interrupts +#endif + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + MOVS r3, #0 // Build NULL value + LDR r1, [r0] // Pickup current thread pointer + + /* Determine if there is a current thread to finish preserving. */ + CMP r1, #0 + BEQ __tx_ts_new // If NULL, skip preservation + + /* Recover PSP and preserve current thread context. */ + + STR r3, [r0] // Set _tx_thread_current_ptr to NULL + MRS r3, PSP // Pickup PSP pointer (thread's stack pointer) + SUBS r3, r3, #16 // Allocate stack space + STM r3!, {r4-r7} // Save its remaining registers + MOV r4, r8 // Pick up r8 + MOV r5, r9 // Pick up r9 + MOV r6, r10 // Pick up r10 + MOV r7, r11 // Pick up r11 + SUBS r3, r3, #32 // Allocate stack space for r8-r11 + STM r3!, {r4-r7} // Save r8-r11 + SUBS r3, r3, #20 // Allocate stack space for lr + MOV r5, lr // Move lr into r5 + STR r5, [r3] // Save lr + STR r3, [r1, #8] // Save the thread stack pointer + + /* Determine if time-slice is active. If it isn't, skip time handling processing. */ + + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r4] // Pickup current time-slice + CMP r5, #0 + BEQ __tx_ts_new // If not active, skip processing + + /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable. */ + + STR r5, [r1, #24] // Save current time-slice + + /* Clear the global time-slice. */ + MOVS r3, #0 // Build NULL value + STR r3, [r4] // Clear time-slice + + /* Executing thread is now completely preserved!!! */ + +__tx_ts_new: + + /* Now we are looking for a new thread to execute! */ + + CPSID i // Disable interrupts + LDR r1, [r2] // Is there another thread ready to execute? + CMP r1, #0 + BNE __tx_ts_restore // Yes, schedule it + + /* The following is the idle wait processing... in this case, no threads are ready for execution and the + system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts + are disabled to allow use of WFI for waiting for a thread to arrive. */ + +__tx_ts_wait: + CPSID i // Disable interrupts + LDR r1, [r2] // Pickup the next thread to execute pointer + CMP r1, #0 + BNE __tx_ts_ready // If non-NULL, a new thread is ready! +#ifdef TX_ENABLE_WFI + DSB // Ensure no outstanding memory transactions + WFI // Wait for interrupt + ISB // Ensure pipeline is flushed +#endif + CPSIE i // Enable interrupts + B __tx_ts_wait // Loop to continue waiting + + /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are + already in the handler! */ + +__tx_ts_ready: + LDR r7, =0x08000000 // Build clear PendSV value + LDR r6, =0xE000ED04 // Build base NVIC address + STR r7, [r6] // Clear any PendSV + +__tx_ts_restore: + + /* A thread is ready, make the current thread the new thread + and enable interrupts. */ + + STR r1, [r0] // Setup the current thread pointer to the new thread + CPSIE i // Enable interrupts + + /* Increment the thread run count. */ + + LDR r7, [r1, #4] // Pickup the current thread run count + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r1, #24] // Pickup thread's current time-slice + ADDS r7, r7, #1 // Increment the thread run count + STR r7, [r1, #4] // Store the new run count + + /* Setup global time-slice with thread's current time-slice. */ + + STR r5, [r4] // Setup global time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread entry function to indicate the thread is executing. */ + PUSH {r0, r1} // Save r0 and r1 + BL _tx_execution_thread_enter // Call the thread execution enter function + POP {r0, r1} // Recover r0 and r1 +#endif + + /* Restore the thread context and PSP. */ + + LDR r3, [r1, #8] // Pickup thread's stack pointer + + // Set up CONTROL register based on user mode flag (privileged/unprivileged mode) + MRS r5, CONTROL // Pickup current CONTROL register + MOVS r6, #0x98 + LDR r4, [r1, r6] // Pickup current user mode flag + MOVS r6, #1 + BICS r5, r5, r6 // Clear the UNPRIV bit + ORRS r4, r4, r5 // Build new CONTROL register + MSR CONTROL, r4 // Setup new CONTROL register + + // Determine if MPU needs to be configured + LDR r0, =0xE000ED94 // Build MPU control reg address + MOVS r6, #0 // Build disable value + STR r6, [r0] // Disable MPU + MOVS r6, #0x90 + LDR r0, [r1, r6] // Pickup the module instance pointer + CMP r0, #0 + BEQ skip_mpu_setup // Is this thread owned by a module? No, skip MPU setup + MOV r8, r1 // Copy thread ptr + LDR r1, [r0, #0x64] // Pickup MPU register[0] + CMP r1, #0 + BEQ skip_mpu_setup // Is protection required for this module? No, skip MPU setup + LDR r1, =0xE000ED9C // Build address of MPU base register + + // Initialize loop to configure MPU registers + // Order doesn't matter, so txm_module_instance_mpu_registers[0] + // will be in region 7 and txm_module_instance_mpu_registers[7] will be in region 0. + MOVS r3, #0x64 // Index of MPU register settings in thread control block + ADD r0, r0, r3 // Build address of MPU register start in thread control block + MOVS r5, #0 // Select region 0 + LDR r4, =0xE000ED98 // Region register address + // Loop to load MPU registers +_tx_mpu_loop: + LDR r1, =0xE000ED9C // Build address of MPU base register + STR r5, [r4] // Set region + LDM r0!, {r2-r3} // Get MPU settings from the module + STM r1!, {r2-r3} // Set MPU registers for region + ADDS r5, r5, #1 // Increment to next region + CMP r5, #8 // Check if all regions have been set + BNE _tx_mpu_loop + + LDR r0, =0xE000ED94 // Build MPU control reg address + MOVS r1, #5 // Build enable value with background region enabled + STR r1, [r0] // Enable MPU + MOV r1, r8 // Get copied thread ptr + +skip_mpu_setup: + + // Restore the thread context and PSP + LDR r3, [r1, #8] // Pickup thread's stack pointer + LDR r5, [r3] // Recover saved LR + ADDS r3, r3, #4 // Position past LR + MOV lr, r5 // Restore LR + LDM r3!, {r4-r7} // Recover thread's registers (r8-r11) + MOV r11, r7 + MOV r10, r6 + MOV r9, r5 + MOV r8, r4 + LDM r3!, {r4-r7} // Recover thread's registers (r4-r7) + MSR PSP, r3 // Setup the thread's stack pointer + + BX lr // Return to thread! + + + /* SVC Handler. */ + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global SVC_Handler + .thumb_func +.type SVC_Handler, function +SVC_Handler: + .global __tx_SVCallHandler + .thumb_func +__tx_SVCallHandler: + + MRS r0, PSP // Pickup the PSP stack + LDR r1, [r0, #24] // Pickup the point of interrupt + MOV r3, r1 + SUBS r3, r3, #2 + LDRB r2, [r3] // Pickup the SVC parameter + + /* Determine which SVC trap we are processing */ + + CMP r2, #1 // Is it the entry into ThreadX? + BNE _tx_thread_user_return // No, return to user mode + + /* At this point we have an SVC 1, which means we are entering + the kernel from a module thread with user mode selected. */ + + LDR r2, =_txm_module_priv // Load address of where we should have come from + CMP r1, r2 // Did we come from user_mode_entry? + BEQ _tx_entry_continue // If no (not equal), then... + BX lr // return from where we came. +_tx_entry_continue: + LDR r3, [r0, #20] // This is the saved LR + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0 // Build clear value + MOVS r0, #0x98 // Index of current user mode + STR r1, [r2, r0] // Clear the current user mode selection for thread + MOVS r0, #0xA0 // Index of saved LR + STR r3, [r2, r0] // Save the original LR in thread control block + + /* If there is memory protection, use kernel stack */ + MOVS r0, #0x90 // Index of module instance ptr + LDR r0, [r2, r0] // Load the module instance ptr + LDR r0, [r0, #0x0C] // Load the module property flags + MOVS r1, #2 // MPU protection flag + TST r0, r1 // Check if memory protected + BEQ _tx_skip_kernel_stack_enter + + /* Switch to the module thread's kernel stack */ + MOVS r0, #0xA8 // Index of module kernel stack end + LDR r0, [r2, r0] // Load the module kernel stack end + MOVS r1, #0xA4 // Index of module kernel stack start + LDR r1, [r2, r1] // Load the module kernel stack start + +#ifndef TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE + MOVS r3, #0xAC // Index of module kernel stack size + LDR r3, [r2, r3] // Load the module kernel stack size + STR r1, [r2, #12] // Set stack start + STR r0, [r2, #16] // Set stack end + STR r3, [r2, #20] // Set stack size +#endif + + MRS r3, PSP // Pickup thread stack pointer + MOVS r1, #0xB0 // Index of module stack pointer + STR r3, [r2, r1] // Save thread stack pointer + + /* Build kernel stack by copying thread stack two registers at a time */ + SUBS r0, r0, #32 // Start at top of hardware stack + LDMIA r3!, {r1,r2} // Get r0, r1 from thread stack + STMIA r0!, {r1,r2} // Insert r0, r1 into kernel stack + LDMIA r3!, {r1,r2} // Get r2, r3 from thread stack + STMIA r0!, {r1,r2} // Insert r2, r3 into kernel stack + LDMIA r3!, {r1,r2} // Get r12, lr from thread stack + STMIA r0!, {r1,r2} // Insert r12, lr into kernel stack + LDMIA r3!, {r1,r2} // Get pc, xpsr from thread stack + STMIA r0!, {r1,r2} // Insert pc, xpsr into kernel stack + SUBS r0, r0, #32 // Go back to top of stack + + MSR PSP, r0 // Set kernel stack pointer + +_tx_skip_kernel_stack_enter: + MRS r0, CONTROL // Pickup current CONTROL register + MOVS r1, #1 + BICS r0, r0, r1 // Clear the UNPRIV bit + MSR CONTROL, r0 // Setup new CONTROL register + BX lr // Return to thread + +_tx_thread_user_return: + LDR r2, =_txm_module_user_mode_exit // Load address of where we should have come from + CMP r1, r2 // Did we come from user_mode_exit? + BEQ _tx_exit_continue // If no (not equal), then... + BX lr // return from where we came. +_tx_exit_continue: + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0x9C // Index of user mode + MOVS r3, #0x98 // Index of current user mode + LDR r1, [r2, r1] // Pick up user mode + STR r1, [r2, r3] // Set the current user mode selection for thread + + /* If there is memory protection, use kernel stack */ + MOVS r0, #0x90 // Index of module instance ptr + LDR r0, [r2, r0] // Load the module instance ptr + LDR r0, [r0, #0x0C] // Load the module property flags + MOVS r1, #2 // MPU protection flag + TST r0, r1 // Check if memory protected + BEQ _tx_skip_kernel_stack_exit + +#ifndef TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE + MOVS r0, #0xB4 // Index of module thread stack start + LDR r0, [r2, r0] // Load the module thread stack start + MOVS r1, #0xB8 // Index of module thread stack end + LDR r1, [r2, r1] // Load the module thread stack end + MOVS r3, #0xBC // Index of module thread stack size + LDR r3, [r2, r3] // Load the module thread stack size + STR r0, [r2, #12] // Set stack start + STR r1, [r2, #16] // Set stack end + STR r3, [r2, #20] // Set stack size +#endif + MOVS r1, #0xB0 // Index of module thread stack pointer + LDR r0, [r2, r1] // Load the module thread stack pointer + MRS r3, PSP // Pickup kernel stack pointer + + /* Copy kernel hardware stack to module thread stack. */ + LDM r3!, {r1-r2} // Get r0, r1 from kernel stack + STM r0!, {r1-r2} // Insert r0, r1 into thread stack + LDM r3!, {r1-r2} // Get r2, r3 from kernel stack + STM r0!, {r1-r2} // Insert r2, r3 into thread stack + LDM r3!, {r1-r2} // Get r12, lr from kernel stack + STM r0!, {r1-r2} // Insert r12, lr into thread stack + LDM r3!, {r1-r2} // Get pc, xpsr from kernel stack + STM r0!, {r1-r2} // Insert pc, xpsr into thread stack + SUBS r0, r0, #32 // Subtract 32 to get back to top of stack + MSR PSP, r0 // Set thread stack pointer + + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0x9C // Index of user mode + LDR r1, [r2, r1] // Pick up user mode + +_tx_skip_kernel_stack_exit: + MRS r0, CONTROL // Pickup current CONTROL register + ORRS r0, r0, r1 // OR in the user mode bit + MSR CONTROL, r0 // Setup new CONTROL register + BX lr // Return to thread + + /* Kernel entry function from user mode. */ + + .global _txm_module_manager_kernel_dispatch + .align 8 + .syntax unified +// VOID _txm_module_manager_user_mode_entry(VOID) +// { + .global _txm_module_manager_user_mode_entry + .thumb_func +_txm_module_manager_user_mode_entry: + SVC 1 // Enter kernel +_txm_module_priv: + /* At this point, we are out of user mode. The original LR has been saved in the + thread control block. Simply call the kernel dispatch function. */ + BL _txm_module_manager_kernel_dispatch + + /* Pickup the original LR value while still in privileged mode */ + LDR r2, =_tx_thread_current_ptr // Build current thread pointer address + LDR r3, [r2] // Pickup current thread pointer + MOVS r2, #0xA0 + LDR r1, [r3, r2] // Pickup saved LR from original call + MOV lr, r1 + + SVC 2 // Exit kernel and return to user mode +_txm_module_user_mode_exit: + BX lr // Return to the caller + NOP + NOP + NOP + NOP +// } + diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_stack_build.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_stack_build.S new file mode 100644 index 00000000..64467a2a --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_stack_build.S @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .thumb_func +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + MOVS r3, #0x7 + BICS r2, r2, r3 // Align frame for 8-byte alignment + SUBS r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOVS r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r4 + STR r3, [r2, #8] // Store initial r5 + STR r3, [r2, #12] // Store initial r6 + STR r3, [r2, #16] // Store initial r7 + STR r3, [r2, #20] // Store initial r8 + STR r3, [r2, #24] // Store initial r9 + STR r3, [r2, #28] // Store initial r10 + STR r3, [r2, #32] // Store initial r11 + + /* Hardware stack follows. */ + + STR r3, [r2, #36] // Store initial r0 + STR r3, [r2, #40] // Store initial r1 + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + LDR r3, =0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + LDR r3, =0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's + // control block + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_system_return.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_system_return.S new file mode 100644 index 00000000..511c117b --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_thread_system_return.S @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .thumb_func +_tx_thread_system_return: + + /* Return to real scheduler via PendSV. Note that this routine is often + replaced with in-line assembly in tx_port.h to improved performance. */ + + LDR r0, =0x10000000 // Load PENDSVSET bit + LDR r1, =0xE000ED04 // Load NVIC base + STR r0, [r1] // Set PENDSVBIT in ICSR + MRS r0, IPSR // Pickup IPSR + CMP r0, #0 // Is it a thread returning? + BNE _isr_context // If ISR, skip interrupt enable + MRS r1, PRIMASK // Thread context returning, pickup PRIMASK + CPSIE i // Enable interrupts + MSR PRIMASK, r1 // Restore original interrupt posture +_isr_context: + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m0+/gnu/module_manager/src/tx_timer_interrupt.S new file mode 100644 index 00000000..7cc12eb7 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/tx_timer_interrupt.S @@ -0,0 +1,242 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* expiration functions are called. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .thumb_func +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR r1, =_tx_timer_system_clock // Pickup address of system clock + LDR r0, [r1, #0] // Pickup system clock + ADDS r0, r0, #1 // Increment system clock + STR r0, [r1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + // if (_tx_timer_time_slice) + // { + + LDR r3, =_tx_timer_time_slice // Pickup address of time-slice + LDR r2, [r3, #0] // Pickup time-slice + CMP r2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + // _tx_timer_time_slice--; + + SUBS r2, r2, #1 // Decrement the time-slice + STR r2, [r3, #0] // Store new time-slice value + + /* Check for expiration. */ + // if (__tx_timer_time_slice == 0) + + CMP r2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + // _tx_timer_expired_time_slice = TX_TRUE; + + LDR r3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOVS r0, #1 // Build expired value + STR r0, [r3, #0] // Set time-slice expiration flag + + // } + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR r1, =_tx_timer_current_ptr // Pickup current timer pointer address + LDR r0, [r1, #0] // Pickup current timer + LDR r2, [r0, #0] // Pickup timer list entry + CMP r2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR r3, =_tx_timer_expired // Pickup expiration flag address + MOVS r2, #1 // Build expired value + STR r2, [r3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADDS r0, r0, #4 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR r3, =_tx_timer_list_end // Pickup addr of timer list end + LDR r2, [r3, #0] // Pickup list end + CMP r0, r2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR r3, =_tx_timer_list_start // Pickup addr of timer list start + LDR r0, [r3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR r0, [r1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR r2, [r3, #0] // Pickup time-slice expired flag + CMP r2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR r1, =_tx_timer_expired // Pickup addr of other expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CMP r0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + PUSH {r0, lr} // Save the lr register on the stack + // and save r0 just to keep 8-byte alignment + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR r1, =_tx_timer_expired // Pickup addr of expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CMP r0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR r2, [r3, #0] // Pickup the actual flag + CMP r2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + LDR r0, =_tx_thread_preempt_disable // Build address of preempt disable flag + LDR r1, [r0] // Is the preempt disable flag set? + CMP r1, #0 // + BNE __tx_timer_skip_time_slice // Yes, skip the PendSV logic + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + LDR r3, [r2] // Pickup the execute thread pointer + LDR r0, =0xE000ED04 // Build address of control register + LDR r2, =0x10000000 // Build value for PendSV bit + CMP r1, r3 // Are they the same? + BEQ __tx_timer_skip_time_slice // If the same, there was no time-slice performed + STR r2, [r0] // Not the same, issue the PendSV for preemption +__tx_timer_skip_time_slice: + + // } + +__tx_timer_not_ts_expiration: + + POP {r0, r1} // Recover lr register (r0 is just there for + MOV lr, r1 // the 8-byte stack alignment + + // } + +__tx_timer_nothing_expired: + + DSB // Complete all memory access + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_alignment_adjust.c b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_alignment_adjust.c new file mode 100644 index 00000000..6c8b3b57 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_alignment_adjust.c @@ -0,0 +1,448 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_power_of_two_block_size Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates a power of two size at or immediately above*/ +/* the input size and returns it to the caller. */ +/* */ +/* INPUT */ +/* */ +/* size Block size */ +/* */ +/* OUTPUT */ +/* */ +/* calculated size Rounded up to power of two */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_alignment_adjust Adjust alignment for Cortex-M */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_power_of_two_block_size(ULONG size) +{ + /* Check for 0 size. */ + if(size == 0) + return 0; + + /* Minimum MPU block size is 256. */ + if(size <= 256) + return 256; + + /* Bit twiddling trick to round to next high power of 2 + (if original size is power of 2, it will return original size. Perfect!) */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + /* Return a power of 2 size at or above the input size. */ + return(size); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_alignment_adjust Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adjusts the alignment and size of the code and data */ +/* section for a given module implementation. */ +/* */ +/* INPUT */ +/* */ +/* module_preamble Pointer to module preamble */ +/* code_size Size of the code area (updated) */ +/* code_alignment Code area alignment (updated) */ +/* data_size Size of data area (updated) */ +/* data_alignment Data area alignment (updated) */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_power_of_two_block_size Calculate power of two size */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, + ULONG *code_size, + ULONG *code_alignment, + ULONG *data_size, + ULONG *data_alignment) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU +ULONG local_code_size; +ULONG local_code_alignment; +ULONG local_data_size; +ULONG local_data_alignment; +ULONG code_size_accum; +ULONG data_size_accum; + + /* Copy the input parameters into local variables for ease of use. */ + local_code_size = *code_size; + local_code_alignment = *code_alignment; + local_data_size = *data_size; + local_data_alignment = *data_alignment; + + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_code_alignment = _txm_power_of_two_block_size(local_code_size) >> 2; + code_size_accum = local_code_alignment + local_code_alignment; + code_size_accum = code_size_accum + (_txm_power_of_two_block_size(local_code_size - code_size_accum) >> 1); + code_size_accum = code_size_accum + _txm_power_of_two_block_size(local_code_size - code_size_accum); + local_code_size = code_size_accum; + + /* Determine data block sizes. Minimize the alignment requirement. + There are 4 MPU data entries available. The following is how the data size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to data size. + 2. 1/4 of the largest power of two that is greater than or equal to data size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_data_alignment = _txm_power_of_two_block_size(local_data_size) >> 2; + data_size_accum = local_data_alignment + local_data_alignment; + data_size_accum = data_size_accum + (_txm_power_of_two_block_size(local_data_size - data_size_accum) >> 1); + data_size_accum = data_size_accum + _txm_power_of_two_block_size(local_data_size - data_size_accum); + local_data_size = data_size_accum; + + /* Return all the information to the caller. */ + *code_size = local_code_size; + *code_alignment = local_code_alignment; + *data_size = local_data_size; + *data_alignment = local_data_alignment; + +#else + +ULONG local_code_size; +ULONG local_code_alignment; +ULONG local_data_size; +ULONG local_data_alignment; +ULONG code_block_size; +ULONG data_block_size; +ULONG code_size_accum; +ULONG data_size_accum; + + /* Copy the input parameters into local variables for ease of use. */ + local_code_size = *code_size; + local_code_alignment = *code_alignment; + local_data_size = *data_size; + local_data_alignment = *data_alignment; + + + /* Test for external memory enabled in preamble. */ + if(module_preamble -> txm_module_preamble_property_flags & TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS) + { + /* External/shared memory enabled. TXM_MODULE_MANAGER_CODE_MPU_ENTRIES-1 code entries will be used. */ + if (local_code_size <= (32*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 32 is best. */ + code_block_size = 32; + } + else if (local_code_size <= (64*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 64 is best. */ + code_block_size = 64; + } + else if (local_code_size <= (128*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 128 is best. */ + code_block_size = 128; + } + else if (local_code_size <= (256*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 256 is best. */ + code_block_size = 256; + } + else if (local_code_size <= (512*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 512 is best. */ + code_block_size = 512; + } + else if (local_code_size <= (1024*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 1024 is best. */ + code_block_size = 1024; + } + else if (local_code_size <= (2048*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 2048 is best. */ + code_block_size = 2048; + } + else if (local_code_size <= (4096*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 4096 is best. */ + code_block_size = 4096; + } + else if (local_code_size <= (8192*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 8192 is best. */ + code_block_size = 8192; + } + else if (local_code_size <= (16384*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 16384 is best. */ + code_block_size = 16384; + } + else if (local_code_size <= (32768*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 32768 is best. */ + code_block_size = 32768; + } + else if (local_code_size <= (65536*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 65536 is best. */ + code_block_size = 65536; + } + else if (local_code_size <= (131072*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 131072 is best. */ + code_block_size = 131072; + } + else if (local_code_size <= (262144*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 262144 is best. */ + code_block_size = 262144; + } + else if (local_code_size <= (524288*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 524288 is best. */ + code_block_size = 524288; + } + else if (local_code_size <= (1048576*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 1048576 is best. */ + code_block_size = 1048576; + } + else if (local_code_size <= (2097152*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 2097152 is best. */ + code_block_size = 2097152; + } + else if (local_code_size <= (4194304*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 4194304 is best. */ + code_block_size = 4194304; + } + else + { + /* Just set block size to 32MB just to create an allocation error! */ + code_block_size = 33554432; + } + + /* Calculate the new code size. */ + local_code_size = code_block_size*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1); + + /* Determine if the code block size is greater than the current alignment. If so, use block size + as the alignment. */ + if (code_block_size > local_code_alignment) + local_code_alignment = code_block_size; + + } + else + { + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_code_alignment = _txm_power_of_two_block_size(local_code_size) >> 2; + code_size_accum = local_code_alignment + local_code_alignment; + code_size_accum = code_size_accum + (_txm_power_of_two_block_size(local_code_size - code_size_accum) >> 1); + code_size_accum = code_size_accum + _txm_power_of_two_block_size(local_code_size - code_size_accum); + local_code_size = code_size_accum; + } + + /* Determine the best data block size, which in our case is the minimal alignment. */ + if (local_data_size <= (32*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 32 is best. */ + data_block_size = 32; + } + else if (local_data_size <= (64*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 64 is best. */ + data_block_size = 64; + } + else if (local_data_size <= (128*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 128 is best. */ + data_block_size = 128; + } + else if (local_data_size <= (256*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 256 is best. */ + data_block_size = 256; + } + else if (local_data_size <= (512*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 512 is best. */ + data_block_size = 512; + } + else if (local_data_size <= (1024*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 1024 is best. */ + data_block_size = 1024; + } + else if (local_data_size <= (2048*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 2048 is best. */ + data_block_size = 2048; + } + else if (local_data_size <= (4096*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 4096 is best. */ + data_block_size = 4096; + } + else if (local_data_size <= (8192*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 8192 is best. */ + data_block_size = 8192; + } + else if (local_data_size <= (16384*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 16384 is best. */ + data_block_size = 16384; + } + else if (local_data_size <= (32768*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 32768 is best. */ + data_block_size = 32768; + } + else if (local_data_size <= (65536*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 65536 is best. */ + data_block_size = 65536; + } + else if (local_data_size <= (131072*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 131072 is best. */ + data_block_size = 131072; + } + else if (local_data_size <= (262144*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 262144 is best. */ + data_block_size = 262144; + } + else if (local_data_size <= (524288*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 524288 is best. */ + data_block_size = 524288; + } + else if (local_data_size <= (1048576*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 1048576 is best. */ + data_block_size = 1048576; + } + else if (local_data_size <= (2097152*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 2097152 is best. */ + data_block_size = 2097152; + } + else if (local_data_size <= (4194304*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 4194304 is best. */ + data_block_size = 4194304; + } + else + { + /* Just set data block size to 32MB just to create an allocation error! */ + data_block_size = 33554432; + } + + /* Calculate the new data size. */ + data_size_accum = data_block_size; + while(data_size_accum < local_data_size) + { + data_size_accum += data_block_size; + } + local_data_size = data_size_accum; + + /* Determine if the data block size is greater than the current alignment. If so, use block size + as the alignment. */ + if (data_block_size > local_data_alignment) + { + local_data_alignment = data_block_size; + } + + /* Return all the information to the caller. */ + *code_size = local_code_size; + *code_alignment = local_code_alignment; + *data_size = local_data_size; + *data_alignment = local_data_alignment; + +#endif +} diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_external_memory_enable.c b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_external_memory_enable.c new file mode 100644 index 00000000..1386362a --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_external_memory_enable.c @@ -0,0 +1,295 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_mutex.h" +#include "tx_queue.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_external_memory_enable Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates an entry in the MPU table for a shared */ +/* memory space. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* start_address Start address of memory */ +/* length Length of external memory */ +/* attributes Memory attributes (r/w) */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_mutex_get Get protection mutex */ +/* _tx_mutex_put Release protection mutex */ +/* _txm_power_of_two_block_size Round length to power of two */ +/* _txm_module_manager_mm_register_setup Reconfigure MPU registers */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_external_memory_enable(TXM_MODULE_INSTANCE *module_instance, + VOID *start_address, + ULONG length, + UINT attributes) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU +ULONG block_size; +ULONG region_size; +ULONG srd_bits; +ULONG size_register; +ULONG address; +ULONG shared_index; +ULONG attributes_check = 0; + + /* Determine if the module manager has not been initialized yet. */ + if (_txm_module_manager_ready != TX_TRUE) + { + /* Module manager has not been initialized. */ + return(TX_NOT_AVAILABLE); + } + + /* Determine if the module is valid. */ + if (module_instance == TX_NULL) + { + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Get module manager protection mutex. */ + _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); + + /* Determine if the module instance is valid. */ + if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Determine if the module instance is in the loaded state. */ + if (module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if the module is not ready. */ + return(TX_START_ERROR); + } + + /* Determine if there are shared memory entries available. */ + if(module_instance -> txm_module_instance_shared_memory_count >= TXM_MODULE_MPU_SHARED_ENTRIES) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* No more entries available. */ + return(TX_NO_MEMORY); + } + + /* Start address and length must adhere to Cortex-M7 MPU. + The address must align with the block size. */ + + block_size = _txm_power_of_two_block_size(length); + address = (ULONG) start_address; + if(address != (address & ~(block_size - 1))) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return alignment error. */ + return(TXM_MODULE_ALIGNMENT_ERROR); + } + + /* At this point, we have a valid address and block size. + Set up MPU registers. */ + + /* Pick up index into shared memory entries. */ + shared_index = TXM_MODULE_MPU_SHARED_INDEX + module_instance -> txm_module_instance_shared_memory_count; + + /* Save address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[shared_index].txm_module_mpu_region_address = address | shared_index | 0x10; + + /* Calculate the region size. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Calculate the subregion bits. */ + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, length); + + /* Generate SRD, size, and enable attributes. */ + size_register = srd_bits | region_size | TXM_ENABLE_REGION | TXM_MODULE_MPU_SHARED_ACCESS_CONTROL; + + /* Check for optional write attribute. */ + if(attributes & TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE) + { + attributes_check = TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT; + } + + /* Save attribute-size register. */ + module_instance -> txm_module_instance_mpu_registers[shared_index].txm_module_mpu_region_attribute_size = attributes_check | size_register; + + /* Keep track of shared memory address and length in module instance. */ + module_instance -> txm_module_instance_shared_memory_address[module_instance -> txm_module_instance_shared_memory_count] = address; + module_instance -> txm_module_instance_shared_memory_length[module_instance -> txm_module_instance_shared_memory_count] = length; + + /* Increment counter. */ + module_instance -> txm_module_instance_shared_memory_count++; + + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return success. */ + return(TX_SUCCESS); + +#else + +ULONG block_size; +ULONG region_size; +ULONG subregion_bits; +ULONG address; +UINT attributes_check = 0; +TXM_MODULE_PREAMBLE *module_preamble; + + /* Determine if the module manager has not been initialized yet. */ + if (_txm_module_manager_ready != TX_TRUE) + { + /* Module manager has not been initialized. */ + return(TX_NOT_AVAILABLE); + } + + /* Determine if the module is valid. */ + if (module_instance == TX_NULL) + { + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Get module manager protection mutex. */ + _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); + + /* Determine if the module instance is valid. */ + if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Determine if the module instance is in the loaded state. */ + if (module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if the module is not ready. */ + return(TX_START_ERROR); + } + + /* Check if preamble shared mem and mem protection property bits are set. */ + module_preamble = module_instance -> txm_module_instance_preamble_ptr; + if((module_preamble -> txm_module_preamble_property_flags & (TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS)) + != (TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS)) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if bit not set. */ + return(TXM_MODULE_INVALID_PROPERTIES); + } + + /* Start address and length must adhere to Cortex-M MPU. + The address must align with the block size. */ + + block_size = _txm_power_of_two_block_size(length); + address = (ULONG) start_address; + if(address != (address & ~(block_size - 1))) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return alignment error. */ + return(TXM_MODULE_ALIGNMENT_ERROR); + } + + /* At this point, we have a valid address and block size. + Set up MPU registers. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX] = address | TXM_MODULE_MANAGER_SHARED_MPU_REGION | 0x10; + + /* Calculate the region size. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + /* Calculate the subregion bits. */ + subregion_bits = _txm_module_manager_calculate_srd_bits(block_size, length); + + /* Check for valid attributes. */ + if(attributes & TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE) + { + attributes_check = TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT; + } + + /* Build register with attributes. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX+1] = region_size | subregion_bits | attributes_check | 0x12070001; + + /* Keep track of shared memory address and length in module instance. */ + module_instance -> txm_module_instance_shared_memory_address = address; + module_instance -> txm_module_instance_shared_memory_length = length; + + /* Recalculate MPU settings. */ + _txm_module_manager_mm_register_setup(module_instance); + + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return success. */ + return(TX_SUCCESS); + +#endif +} diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_handler.c b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_handler.c new file mode 100644 index 00000000..a00a3325 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_handler.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/* Define the user's fault notification callback function pointer. This is + setup via the txm_module_manager_memory_fault_notify API. */ + +VOID (*_txm_module_manager_fault_notify)(TX_THREAD *, TXM_MODULE_INSTANCE *); + + +/* Define a macro that can be used to allocate global variables useful to + store information about the last fault. This macro is defined in + txm_module_port.h and is usually populated in the assembly language + fault handling prior to the code calling _txm_module_manager_memory_fault_handler. */ + +TXM_MODULE_MANAGER_FAULT_INFO + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_memory_fault_handler Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles a fault associated with a memory protected */ +/* module. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_terminate Terminate thread */ +/* */ +/* CALLED BY */ +/* */ +/* Fault handler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_memory_fault_handler(VOID) +{ + +TXM_MODULE_INSTANCE *module_instance_ptr; +TX_THREAD *thread_ptr; + + /* Pickup the current thread. */ + thread_ptr = _tx_thread_current_ptr; + + /* Initialize the module instance pointer to NULL. */ + module_instance_ptr = TX_NULL; + + /* Is there a thread? */ + if (thread_ptr) + { + /* Pickup the module instance. */ + module_instance_ptr = thread_ptr -> tx_thread_module_instance_ptr; + + /* Terminate the current thread. */ + _tx_thread_terminate(_tx_thread_current_ptr); + } + + /* Determine if there is a user memory fault notification callback. */ + if (_txm_module_manager_fault_notify) + { + /* Yes, call the user's notification memory fault callback. */ + (_txm_module_manager_fault_notify)(thread_ptr, module_instance_ptr); + } +} diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_notify.c b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_notify.c new file mode 100644 index 00000000..3c44ee61 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_memory_fault_notify.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/* Define the external user's fault notification callback function pointer. This is + setup via the txm_module_manager_memory_fault_notify API. */ + +extern VOID (*_txm_module_manager_fault_notify)(TX_THREAD *, TXM_MODULE_INSTANCE *); + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_memory_fault_notify Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers an application callback when/if a memory */ +/* fault occurs. The supplied thread is automatically terminated, but */ +/* any other threads in the same module may still execute. */ +/* */ +/* INPUT */ +/* */ +/* notify_function Memory fault notification */ +/* function, NULL disables. */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_memory_fault_notify(VOID (*notify_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +{ + /* Setup notification function. */ + _txm_module_manager_fault_notify = notify_function; + + /* Return success. */ + return(TX_SUCCESS); +} diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_mm_register_setup.c b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_mm_register_setup.c new file mode 100644 index 00000000..9882e1ff --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_mm_register_setup.c @@ -0,0 +1,797 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_region_size_get Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts the region size in bytes to the block size */ +/* for the Cortex-M0+ MPU specification. */ +/* */ +/* INPUT */ +/* */ +/* block_size Size of the block in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* MPU size specification */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_mm_register_setup */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_module_manager_region_size_get(ULONG block_size) +{ + +ULONG return_value; + + /* Process relative to the input block size. */ + if (block_size == 32) + { + return_value = 0x04; + } + else if (block_size == 64) + { + return_value = 0x05; + } + else if (block_size == 128) + { + return_value = 0x06; + } + else if (block_size == 256) + { + return_value = 0x07; + } + else if (block_size == 512) + { + return_value = 0x08; + } + else if (block_size == 1024) + { + return_value = 0x09; + } + else if (block_size == 2048) + { + return_value = 0x0A; + } + else if (block_size == 4096) + { + return_value = 0x0B; + } + else if (block_size == 8192) + { + return_value = 0x0C; + } + else if (block_size == 16384) + { + return_value = 0x0D; + } + else if (block_size == 32768) + { + return_value = 0x0E; + } + else if (block_size == 65536) + { + return_value = 0x0F; + } + else if (block_size == 131072) + { + return_value = 0x10; + } + else if (block_size == 262144) + { + return_value = 0x11; + } + else if (block_size == 524288) + { + return_value = 0x12; + } + else if (block_size == 1048576) + { + return_value = 0x13; + } + else if (block_size == 2097152) + { + return_value = 0x14; + } + else + { + /* Max 4MB MPU pages for modules. */ + return_value = 0x15; + } + + return(return_value); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_calculate_srd_bits Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates the SRD bits that need to be set to */ +/* protect "length" bytes in a block. */ +/* */ +/* INPUT */ +/* */ +/* block_size Size of the block in bytes */ +/* length Actual length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* SRD bits to be OR'ed with region attribute register. */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_mm_register_setup */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length) +{ + +ULONG srd_bits = 0; +UINT srd_bit_index; + + /* length is smaller than block_size, set SRD bits if block_size is 256 or more. */ + if((block_size >= 256) && (length < block_size)) + { + /* Divide block_size by 8 by shifting right 3. Result is size of subregion. */ + block_size = block_size >> 3; + + /* Set SRD index into attribute register. */ + srd_bit_index = 8; + + /* If subregion overlaps length, move to the next subregion. */ + while(length > block_size) + { + length = length - block_size; + srd_bit_index++; + } + /* Check for a portion of code remaining. */ + if(length) + { + srd_bit_index++; + } + + /* Set unused subregion bits. */ + while(srd_bit_index < 16) + { + srd_bits = srd_bits | (0x1 << srd_bit_index); + srd_bit_index++; + } + } + + return(srd_bits); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_mm_register_setup Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets up the MPU register definitions based on the */ +/* module's memory characteristics. */ +/* */ +/* Default MPU layout: */ +/* Entry Description */ +/* 0 Kernel mode entry */ +/* 1 Module code region */ +/* 2 Module code region */ +/* 3 Module code region */ +/* 4 Module code region */ +/* 5 Module data region */ +/* 6 Module data region */ +/* 7 Module data region */ +/* */ +/* If TXM_MODULE_MANAGER_16_MPU is defined, there are 16 MPU slots. */ +/* MPU layout for the Cortex-M7: */ +/* Entry Description */ +/* 0 Kernel mode entry */ +/* 1 Module code region */ +/* 2 Module code region */ +/* 3 Module code region */ +/* 4 Module code region */ +/* 5 Module data region */ +/* 6 Module data region */ +/* 7 Module data region */ +/* 8 Module data region */ +/* 9 Module shared memory region */ +/* 10 Module shared memory region */ +/* 11 Module shared memory region */ +/* 12 Unused region */ +/* 13 Unused region */ +/* 14 Unused region */ +/* 15 Unused region */ +/* */ +/* */ +/* INPUT */ +/* */ +/* module_instance Pointer to module instance */ +/* */ +/* OUTPUT */ +/* */ +/* MPU specifications for module in module_instance */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_region_size_get */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_thread_create */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU + +ULONG code_address; +ULONG code_size; +ULONG data_address; +ULONG data_size; +ULONG start_stop_stack_size; +ULONG callback_stack_size; +ULONG block_size; +ULONG region_size; +ULONG srd_bits = 0; +UINT mpu_table_index; +UINT i; + + + /* Setup the first MPU region for kernel mode entry. */ + /* Set address register to user mode entry function address, which is guaranteed to be at least 256-byte aligned. + Mask address to proper range, region 0, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_address = ((ULONG) _txm_module_manager_user_mode_entry & 0xFFFFFFE0) | 0x10; + /* Set the attributes, size (256 bytes) and enable bit. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_CODE_ACCESS_CONTROL | (_txm_module_manager_region_size_get(256) << 1) | TXM_ENABLE_REGION; + /* End of kernel mode entry setup. */ + + /* Setup code protection. */ + + /* Initialize the MPU table index. */ + mpu_table_index = 1; + + /* Pickup code starting address and actual size. */ + code_address = (ULONG) module_instance -> txm_module_instance_code_start; + code_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size; + + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MPU_CODE_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to code size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(code_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from code_size to calculate remaining space. */ + code_size = code_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(code_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + code_size = code_size - block_size; + block_size = _txm_power_of_two_block_size(code_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = (code_address & ~(block_size - 1)) | mpu_table_index | 0x10; + /* Build the attribute-size register with permissions, SRD, size, enable. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_CODE_ACCESS_CONTROL | srd_bits | region_size | TXM_ENABLE_REGION; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Increment MPU table index. */ + mpu_table_index++; + } + /* End of code protection. */ + + /* Setup data protection. */ + + /* Reset SRD bitfield. */ + srd_bits = 0; + + /* Pickup data starting address and actual size. */ + data_address = (ULONG) module_instance -> txm_module_instance_data_start; + + /* Adjust the size of the module elements to be aligned to the default alignment. We do this + so that when we partition the allocated memory, we can simply place these regions right beside + each other without having to align their pointers. Note this only works when they all have + the same alignment. */ + + data_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size; + start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size; + callback_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size; + + data_size = ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + start_stop_stack_size = ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + callback_stack_size = ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + /* Update the data size to include thread stacks. */ + data_size = data_size + start_stop_stack_size + callback_stack_size; + + /* Determine data block sizes. Minimize the alignment requirement. + There are 4 MPU data entries available. The following is how the data size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to data size. + 2. 1/4 of the largest power of two that is greater than or equal to data size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the data area. */ + for (i = 0; i < TXM_MODULE_MPU_DATA_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to data size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(data_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from data_size to calculate remaining space. */ + data_size = data_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(data_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + data_size = data_size - block_size; + block_size = _txm_power_of_two_block_size(data_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size); + } + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = (data_address & ~(block_size - 1)) | mpu_table_index | 0x10; + /* Build the attribute-size register with permissions, SRD, size, enable. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_DATA_ACCESS_CONTROL | srd_bits | region_size | TXM_ENABLE_REGION; + + /* Adjust the data address. */ + data_address = data_address + block_size; + + /* Increment MPU table index. */ + mpu_table_index++; + } + + /* Setup MPU for the remaining regions. */ + while (mpu_table_index < TXM_MODULE_MPU_TOTAL_ENTRIES) + { + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = mpu_table_index | 0x10; + + /* Increment MPU table index. */ + mpu_table_index++; + } + +#else + +ULONG code_address; +ULONG code_size; +ULONG data_address; +ULONG data_size; +ULONG start_stop_stack_size; +ULONG callback_stack_size; +ULONG block_size; +ULONG base_address_register; +ULONG base_attribute_register; +ULONG region_size; +ULONG srd_bits = 0; +UINT mpu_register = 0; +UINT mpu_table_index; +UINT i; + + + /* Setup the first region for the ThreadX trampoline code. */ + /* Set base register to user mode entry, which is guaranteed to be at least 256-byte aligned. */ + base_address_register = (ULONG) _txm_module_manager_user_mode_entry; + + /* Mask address to proper range, region 0, set Valid bit. */ + base_address_register = (base_address_register & 0xFFFFFF00) | mpu_register | 0x10; + module_instance -> txm_module_instance_mpu_registers[0] = base_address_register; + + /* Attributes: read only, write-back, shareable, size 256 bytes, region enabled. */ + module_instance -> txm_module_instance_mpu_registers[1] = 0x0607000F; + + /* Initialize the MPU register. */ + mpu_register = 1; + + /* Initialize the MPU table index. */ + mpu_table_index = 2; + + /* Setup values for code area. */ + code_address = (ULONG) module_instance -> txm_module_instance_code_start; + code_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size; + + /* Check if shared memory was set up. If so, only 3 entries are available for + code protection. If not set up, 4 code entries are available. */ + if(module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX] == 0) + { + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MANAGER_CODE_MPU_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to code size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(code_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from code_size to calculate remaining space. */ + code_size = code_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(code_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + code_size = code_size - block_size; + block_size = _txm_power_of_two_block_size(code_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Build the base address register. */ + base_address_register = (code_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x06070001; + + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + } + + /* Only 3 code entries available. */ + else + { + /* Calculate block size, one code entry taken up by shared memory. */ + block_size = _txm_power_of_two_block_size(code_size / (TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1)); + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1; i++) + { + /* Build the base address register. */ + base_address_register = (code_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Check if SRD bits need to be set. */ + if (code_size < block_size) + { + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x06070000; + + /* Is there still some code? If so set the region enable bit. */ + if (code_size) + { + /* Set the region enable bit. */ + base_attribute_register = base_attribute_register | 0x1; + } + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Decrement the code size. */ + if (code_size > block_size) + { + code_size = code_size - block_size; + } + else + { + code_size = 0; + } + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + + /* Adjust indeces to pass over the shared memory entry. */ + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + + /* Setup values for data area. */ + data_address = (ULONG) module_instance -> txm_module_instance_data_start; + + /* Adjust the size of the module elements to be aligned to the default alignment. We do this + so that when we partition the allocated memory, we can simply place these regions right beside + each other without having to align their pointers. Note this only works when they all have + the same alignment. */ + + data_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size; + start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size; + callback_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size; + + data_size = ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + start_stop_stack_size = ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + callback_stack_size = ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + /* Update the data size to include thread stacks. */ + data_size = data_size + start_stop_stack_size + callback_stack_size; + + block_size = _txm_power_of_two_block_size(data_size / TXM_MODULE_MANAGER_DATA_MPU_ENTRIES); + + /* Reset SRD bitfield. */ + srd_bits = 0; + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Now loop through to setup MPU protection for the data area. */ + for (i = 0; i < TXM_MODULE_MANAGER_DATA_MPU_ENTRIES; i++) + { + /* Build the base address register. */ + base_address_register = (data_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Check if SRD bits need to be set. */ + if (data_size < block_size) + { + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size); + } + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x13070000; + + /* Is there still some data? If so set the region enable bit. */ + if (data_size) + { + /* Set the region enable bit. */ + base_attribute_register = base_attribute_register | 0x1; + } + + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the data address. */ + data_address = data_address + block_size; + + /* Decrement the data size. */ + if (data_size > block_size) + { + data_size = data_size - block_size; + } + else + { + data_size = 0; + } + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + +#endif +} + +#ifdef TXM_MODULE_MANAGER_16_MPU +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_inside_data_check Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if the specified object is inside shared */ +/* memory. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Pointer to module instance */ +/* obj_ptr Pointer to the object */ +/* obj_size Size of the object */ +/* */ +/* OUTPUT */ +/* */ +/* Whether the object is inside the shared memory region. */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Module dispatch check functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size) +{ + +UINT shared_memory_index; +UINT num_shared_memory_mpu_entries; +ALIGN_TYPE shared_memory_address_start; +ALIGN_TYPE shared_memory_address_end; + + /* Check for overflow. */ + if ((obj_ptr) > ((obj_ptr) + (obj_size))) + { + return(TX_FALSE); + } + + /* Check if the object is inside the module data. */ + if ((obj_ptr >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && + ((obj_ptr + obj_size) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1))) + { + return(TX_TRUE); + } + + /* Check if the object is inside the shared memory. */ + num_shared_memory_mpu_entries = module_instance -> txm_module_instance_shared_memory_count; + for (shared_memory_index = 0; shared_memory_index < num_shared_memory_mpu_entries; shared_memory_index++) + { + + shared_memory_address_start = (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address[shared_memory_index]; + shared_memory_address_end = shared_memory_address_start + module_instance -> txm_module_instance_shared_memory_length[shared_memory_index]; + + if ((obj_ptr >= (ALIGN_TYPE) shared_memory_address_start) && + ((obj_ptr + obj_size) <= (ALIGN_TYPE) shared_memory_address_end)) + { + return(TX_TRUE); + } + } + + return(TX_FALSE); +} +#endif diff --git a/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_thread_stack_build.S b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_thread_stack_build.S new file mode 100644 index 00000000..7ad5af12 --- /dev/null +++ b/ports_module/cortex_m0+/gnu/module_manager/src/txm_module_manager_thread_stack_build.S @@ -0,0 +1,140 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_thread_stack_build Cortex-M0+/GNU */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to shell function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _txm_module_manager_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +// { + .global _txm_module_manager_thread_stack_build + .thumb_func +_txm_module_manager_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + MOVS r3, #0x7 // + BICS r2, r2, r3 // Align frame for 8-byte alignment + SUBS r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOVS r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r8 + STR r3, [r2, #12] // Store initial r10 + STR r3, [r2, #16] // Store initial r11 + STR r3, [r2, #20] // Store initial r4 + STR r3, [r2, #24] // Store initial r5 + STR r3, [r2, #28] // Store initial r6 + STR r3, [r2, #32] // Store initial r7 + + /* Hardware stack follows. */ + + STR r0, [r2, #36] // Store initial r0, which is the thread control block + + LDR r3, [r0, #8] // Pickup thread entry info pointer,which is in the stack pointer position of the thread control block. + // It was setup in the txm_module_manager_thread_create function. It will be overwritten later in this + // function with the actual, initial stack pointer. + STR r3, [r2, #40] // Store initial r1, which is the module entry information. + LDR r3, [r3, #8] // Pickup data base register from the module information + STR r3, [r2, #8] // Store initial r9 (data base register) + MOVS r3, #0 // Clear r3 again + + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + LDR r3, =0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + LDR r3, =0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's control block + BX lr // Return to caller +// } diff --git a/ports_module/cortex_m0+/iar/example_build/azure_rtos.eww b/ports_module/cortex_m0+/iar/example_build/azure_rtos.eww new file mode 100644 index 00000000..e490c85a --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/azure_rtos.eww @@ -0,0 +1,19 @@ + + + + $WS_DIR$\sample_threadx_module_manager.ewp + + + $WS_DIR$\sample_threadx_module.ewp + + + $WS_DIR$\sample_threadx.ewp + + + $WS_DIR$\tx.ewp + + + $WS_DIR$\txm.ewp + + + diff --git a/ports_module/cortex_m0+/iar/example_build/cstartup_M.s b/ports_module/cortex_m0+/iar/example_build/cstartup_M.s new file mode 100644 index 00000000..da53c200 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/cstartup_M.s @@ -0,0 +1,73 @@ + EXTERN __iar_program_start + PUBLIC __vector_table + + SECTION .text:CODE:REORDER(1) + + ;; Keep vector table even if it's not referenced + REQUIRE __vector_table + + THUMB + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + SECTION .intvec:CODE:NOROOT(2) + + DATA + +__vector_table + DCD sfe(CSTACK) + DCD __Reset_Vector + + DCD NMI_Handler + DCD HardFault_Handler + DCD MemManage_Handler + DCD BusFault_Handler + DCD UsageFault_Handler + DCD 0 + DCD 0 + DCD 0 + DCD 0 + DCD SVC_Handler + DCD DebugMon_Handler + DCD 0 + DCD PendSV_Handler + DCD SysTick_Handler + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + + PUBWEAK NMI_Handler + PUBWEAK HardFault_Handler + PUBWEAK MemManage_Handler + PUBWEAK BusFault_Handler + PUBWEAK UsageFault_Handler + PUBWEAK SVC_Handler + PUBWEAK DebugMon_Handler + PUBWEAK PendSV_Handler + PUBWEAK SysTick_Handler + + SECTION .text:CODE:REORDER:NOROOT(2) + THUMB +__Reset_Vector: + CPSID i ; Disable interrupts + LDR r0, =__iar_program_start + BX r0 + +NMI_Handler +HardFault_Handler +MemManage_Handler +BusFault_Handler +UsageFault_Handler +SVC_Handler +DebugMon_Handler +PendSV_Handler +SysTick_Handler +Default_Handler +__default_handler + CALL_GRAPH_ROOT __default_handler, "interrupt" + NOCALL __default_handler + B __default_handler + + END diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx.c b/ports_module/cortex_m0+/iar/example_build/sample_threadx.c new file mode 100644 index 00000000..c67d75d0 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx.c @@ -0,0 +1,385 @@ +/* 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; + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + +/* Define event buffer. */ + +#ifdef TX_ENABLE_EVENT_TRACE +UCHAR trace_buffer[0x10000]; +#endif + + +/* 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; + + +#ifdef TX_ENABLE_EVENT_TRACE + tx_trace_enable(trace_buffer, sizeof(trace_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, 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_module/cortex_m0+/iar/example_build/sample_threadx.ewd b/ports_module/cortex_m0+/iar/example_build/sample_threadx.ewd new file mode 100644 index 00000000..c7d50c1e --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx.ewd @@ -0,0 +1,2974 @@ + + + 3 + + Debug + + ARM + + 1 + + C-SPY + 2 + + 32 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ARMSIM_ID + 2 + + 1 + 1 + 1 + + + + + + + + CADI_ID + 2 + + 0 + 1 + 1 + + + + + + + + + CMSISDAP_ID + 2 + + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GDBSERVER_ID + 2 + + 0 + 1 + 1 + + + + + + + + + + + IJET_ID + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JLINK_ID + 2 + + 16 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LMIFTDI_ID + 2 + + 2 + 1 + 1 + + + + + + + + + + NULINK_ID + 2 + + 0 + 1 + 1 + + + + + + + PEMICRO_ID + 2 + + 3 + 1 + 1 + + + + + + + + STLINK_ID + 2 + + 7 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + THIRDPARTY_ID + 2 + + 0 + 1 + 1 + + + + + + + + TIFET_ID + 2 + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + XDS100_ID + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\FreeRtos\FreeRtosArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\HWRTOSplugin\HWRTOSplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin2.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\RemedyRtosViewer\RemedyRtosViewer.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8b.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8bBE.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\ThreadX\ThreadXArmPlugin.ENU.ewplugin + 1 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\TargetAccessServer\TargetAccessServer.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + + Release + + ARM + + 0 + + C-SPY + 2 + + 32 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ARMSIM_ID + 2 + + 1 + 1 + 0 + + + + + + + + CADI_ID + 2 + + 0 + 1 + 0 + + + + + + + + + CMSISDAP_ID + 2 + + 4 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GDBSERVER_ID + 2 + + 0 + 1 + 0 + + + + + + + + + + + IJET_ID + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JLINK_ID + 2 + + 16 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LMIFTDI_ID + 2 + + 2 + 1 + 0 + + + + + + + + + + NULINK_ID + 2 + + 0 + 1 + 0 + + + + + + + PEMICRO_ID + 2 + + 3 + 1 + 0 + + + + + + + + STLINK_ID + 2 + + 7 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + THIRDPARTY_ID + 2 + + 0 + 1 + 0 + + + + + + + + TIFET_ID + 2 + + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + XDS100_ID + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\FreeRtos\FreeRtosArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\HWRTOSplugin\HWRTOSplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin2.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\RemedyRtosViewer\RemedyRtosViewer.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8b.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8bBE.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\ThreadX\ThreadXArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\TargetAccessServer\TargetAccessServer.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx.ewp b/ports_module/cortex_m0+/iar/example_build/sample_threadx.ewp new file mode 100644 index 00000000..6beccd27 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx.ewp @@ -0,0 +1,2137 @@ + + + 3 + + Debug + + ARM + + 1 + + General + 3 + + 31 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 31 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + $PROJ_DIR$\cstartup_M.s + + + $PROJ_DIR$\sample_threadx.c + + + $PROJ_DIR$\Debug\Exe\tx.a + + + $PROJ_DIR$\tx_initialize_low_level.s + + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx.icf b/ports_module/cortex_m0+/iar/example_build/sample_threadx.icf new file mode 100644 index 00000000..3d48363f --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx.icf @@ -0,0 +1,34 @@ +define symbol __ICFEDIT_intvec_start__ = 0x0; +/*-Memory Regions-*/ +define symbol __ICFEDIT_region_ROM_start__ = 0x80; +define symbol __ICFEDIT_region_ROM_end__ = 0x1FFFF; +define symbol __ICFEDIT_region_RAM_start__ = 0x100000; +define symbol __ICFEDIT_region_RAM_end__ = 0x1FFFFF; +/*-Sizes-*/ +define symbol __ICFEDIT_size_cstack__ = 0x2000; +define symbol __ICFEDIT_size_heap__ = 0x8000; +/**** End of ICF editor section. ###ICF###*/ + +define symbol __ICFEDIT_size_freemem__ = 0x100000; + + +define memory mem with size = 4G; +define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; +define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; +define region RAM_freemem = mem:[from 0x200000 to 0x300000]; + +define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; +define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; + + +initialize by copy { readwrite }; +initialize by copy with packing = none { section __DLIB_PERTHREAD }; // Required in a multi-threaded application +do not initialize { section .noinit }; + +place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; + +place in ROM_region { readonly }; +place in RAM_region { readwrite, + block CSTACK, block HEAP}; + +place in RAM_region { last section FREE_MEM}; diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.c b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.c new file mode 100644 index 00000000..647dcced --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.c @@ -0,0 +1,432 @@ +/* This is a small demo of the high-performance ThreadX kernel running as a module. It includes + examples of eight threads of different priorities, using a message queue, semaphore, mutex, + event flags group, byte pool, and block pool. */ + +/* Specify that this is a module! */ + +#define TXM_MODULE + + +/* Include the ThreadX module definitions. */ + +#include "txm_module.h" + + +/* Define constants. */ + +#define DEMO_STACK_SIZE 512 +#define DEMO_BYTE_POOL_SIZE 6000 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the pool space in the bss section of the module. ULONG is used to + get the word alignment. */ + +ULONG demo_module_pool_space[DEMO_BYTE_POOL_SIZE / 4]; + +/* Define the external memory area. */ + +#define EXTERNAL_MEMORY_SIZE (128) +#define EXTERNAL_MEMORY (0x64005000) + +/* 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; + + +/* 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; +ULONG semaphore_0_puts; +ULONG event_0_sets; +ULONG queue_0_sends; + +/* 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); + +void semaphore_0_notify(TX_SEMAPHORE *semaphore_ptr) +{ + + if (semaphore_ptr == semaphore_0) + semaphore_0_puts++; +} + + +void event_0_notify(TX_EVENT_FLAGS_GROUP *event_flag_group_ptr) +{ + + if (event_flag_group_ptr == event_flags_0) + event_0_sets++; +} + + +void queue_0_notify(TX_QUEUE *queue_ptr) +{ + + if (queue_ptr == queue_0) + queue_0_sends++; +} + + +/* Define the module start function. */ + +void demo_module_start(ULONG id) +{ + +CHAR *pointer; + + /* Allocate all the objects. In MPU mode, modules cannot allocate control blocks within + their own memory area so they cannot corrupt the resident portion of ThreadX by overwriting + the control block(s). */ + txm_module_object_allocate((void*)&thread_0, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_1, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_2, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_3, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_4, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_5, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_6, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&thread_7, sizeof(TX_THREAD)); + txm_module_object_allocate((void*)&queue_0, sizeof(TX_QUEUE)); + txm_module_object_allocate((void*)&semaphore_0, sizeof(TX_SEMAPHORE)); + txm_module_object_allocate((void*)&mutex_0, sizeof(TX_MUTEX)); + txm_module_object_allocate((void*)&event_flags_0, sizeof(TX_EVENT_FLAGS_GROUP)); + txm_module_object_allocate((void*)&byte_pool_0, sizeof(TX_BYTE_POOL)); + txm_module_object_allocate((void*)&block_pool_0, sizeof(TX_BLOCK_POOL)); + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(byte_pool_0, "module byte pool 0", (UCHAR*)demo_module_pool_space, 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module 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, "module queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + tx_queue_send_notify(queue_0, queue_0_notify); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(semaphore_0, "module semaphore 0", 1); + + tx_semaphore_put_notify(semaphore_0, semaphore_0_notify); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(event_flags_0, "module event flags 0"); + + tx_event_flags_set_notify(event_flags_0, event_0_notify); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(mutex_0, "module 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, "module 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) + { + /* Test memory handler. */ + *(ULONG *)EXTERNAL_MEMORY = 0xCDCDCDCD; + + + /* 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_module/cortex_m0+/iar/example_build/sample_threadx_module.ewd b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewd new file mode 100644 index 00000000..21bf663c --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewd @@ -0,0 +1,2974 @@ + + + 3 + + Debug + + ARM + + 1 + + C-SPY + 2 + + 32 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ARMSIM_ID + 2 + + 1 + 1 + 1 + + + + + + + + CADI_ID + 2 + + 0 + 1 + 1 + + + + + + + + + CMSISDAP_ID + 2 + + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GDBSERVER_ID + 2 + + 0 + 1 + 1 + + + + + + + + + + + IJET_ID + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JLINK_ID + 2 + + 16 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LMIFTDI_ID + 2 + + 2 + 1 + 1 + + + + + + + + + + NULINK_ID + 2 + + 0 + 1 + 1 + + + + + + + PEMICRO_ID + 2 + + 3 + 1 + 1 + + + + + + + + STLINK_ID + 2 + + 7 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + THIRDPARTY_ID + 2 + + 0 + 1 + 1 + + + + + + + + TIFET_ID + 2 + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + XDS100_ID + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\FreeRtos\FreeRtosArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\HWRTOSplugin\HWRTOSplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin2.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\RemedyRtosViewer\RemedyRtosViewer.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8b.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8bBE.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\ThreadX\ThreadXArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\TargetAccessServer\TargetAccessServer.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + + Release + + ARM + + 0 + + C-SPY + 2 + + 32 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ARMSIM_ID + 2 + + 1 + 1 + 0 + + + + + + + + CADI_ID + 2 + + 0 + 1 + 0 + + + + + + + + + CMSISDAP_ID + 2 + + 4 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GDBSERVER_ID + 2 + + 0 + 1 + 0 + + + + + + + + + + + IJET_ID + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JLINK_ID + 2 + + 16 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LMIFTDI_ID + 2 + + 2 + 1 + 0 + + + + + + + + + + NULINK_ID + 2 + + 0 + 1 + 0 + + + + + + + PEMICRO_ID + 2 + + 3 + 1 + 0 + + + + + + + + STLINK_ID + 2 + + 7 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + THIRDPARTY_ID + 2 + + 0 + 1 + 0 + + + + + + + + TIFET_ID + 2 + + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + XDS100_ID + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\FreeRtos\FreeRtosArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\HWRTOSplugin\HWRTOSplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin2.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\RemedyRtosViewer\RemedyRtosViewer.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8b.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8bBE.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\ThreadX\ThreadXArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\TargetAccessServer\TargetAccessServer.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewp b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewp new file mode 100644 index 00000000..f9d6cd5e --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.ewp @@ -0,0 +1,2135 @@ + + + 3 + + Debug + + ARM + + 1 + + General + 3 + + 31 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 31 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + $PROJ_DIR$\sample_threadx_module.c + + + $PROJ_DIR$\Debug\Exe\txm.a + + + $PROJ_DIR$\txm_module_preamble.s + + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.icf b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.icf new file mode 100644 index 00000000..8cfe4766 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module.icf @@ -0,0 +1,53 @@ +/*###ICF### Section handled by ICF editor, don't touch! ****/ +/*-Editor annotation file-*/ +/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\a_v1_0.xml" */ +/*-Specials-*/ +define symbol __ICFEDIT_intvec_start__ = 0x0; +/*-Memory Regions-*/ +define symbol __ICFEDIT_region_ROM_start__ = 0x080f0000; +define symbol __ICFEDIT_region_ROM_end__ = 0x080fffff; +define symbol __ICFEDIT_region_RAM_start__ = 0x64002800; +define symbol __ICFEDIT_region_RAM_end__ = 0x64100000; +/*-Sizes-*/ +define symbol __ICFEDIT_size_cstack__ = 0; +define symbol __ICFEDIT_size_svcstack__ = 0; +define symbol __ICFEDIT_size_irqstack__ = 0; +define symbol __ICFEDIT_size_fiqstack__ = 0; +define symbol __ICFEDIT_size_undstack__ = 0; +define symbol __ICFEDIT_size_abtstack__ = 0; +define symbol __ICFEDIT_size_heap__ = 0x1000; +/**** End of ICF editor section. ###ICF###*/ + +define memory mem with size = 4G; +define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; +define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; + +//define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; +//define block SVC_STACK with alignment = 8, size = __ICFEDIT_size_svcstack__ { }; +//define block IRQ_STACK with alignment = 8, size = __ICFEDIT_size_irqstack__ { }; +//define block FIQ_STACK with alignment = 8, size = __ICFEDIT_size_fiqstack__ { }; +//define block UND_STACK with alignment = 8, size = __ICFEDIT_size_undstack__ { }; +//define block ABT_STACK with alignment = 8, size = __ICFEDIT_size_abtstack__ { }; +define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; + +initialize by copy { readwrite }; +do not initialize { section .noinit }; + +//place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; + +define movable block ROPI with alignment = 4, fixed order +{ + ro object txm_module_preamble.o, + ro, + ro data +}; + +define movable block RWPI with alignment = 8, fixed order, static base +{ + rw, + block HEAP +}; + +place in ROM_region { block ROPI }; +place in RAM_region { block RWPI }; + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.c b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.c new file mode 100644 index 00000000..e928709f --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.c @@ -0,0 +1,125 @@ +/* Small demonstration of the ThreadX module manager. */ + +#include "tx_api.h" +#include "txm_module.h" + + +#define DEMO_STACK_SIZE 1024 + +/* Define the ThreadX object control blocks... */ + +TX_THREAD module_manager; +TXM_MODULE_INSTANCE my_module; + + +/* Define the object pool area. */ + +UCHAR object_memory[16384]; + + +/* Define the count of memory faults. */ + +ULONG memory_faults; + +/* Define module manager thread stack address */ +UCHAR module_manager_stack[DEMO_STACK_SIZE]; + +/* Define the module data pool area. */ + +#define MODULE_DATA_SIZE (256 * 1024) +#define MODULE_DATA (0x64000000) + +/* The module code should be loaded here. */ + +#define MODULE_CODE (0x080f0000) + +/* Define the external memory area. */ + +#define EXTERNAL_MEMORY_SIZE (128) +#define EXTERNAL_MEMORY (0x64005000) + +/* Define thread prototypes. */ + +void module_manager_entry(ULONG thread_input); + + +/* Define fault handler. */ + +VOID module_fault_handler(TX_THREAD *thread, TXM_MODULE_INSTANCE *module) +{ + + /* Just increment the fault counter. */ + memory_faults++; +} + +/* 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 = (CHAR*)module_manager_stack; + + + tx_thread_create(&module_manager, "Module Manager Thread", module_manager_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + pointer = pointer + DEMO_STACK_SIZE; +} + + + + +/* Define the test threads. */ + +void module_manager_entry(ULONG thread_input) +{ + + /* Initialize the module manager. */ + txm_module_manager_initialize((VOID *) MODULE_DATA, MODULE_DATA_SIZE); + + txm_module_manager_object_pool_create(object_memory, sizeof(object_memory)); + + /* Register a fault handler. */ + txm_module_manager_memory_fault_notify(module_fault_handler); + + /* Load the module that is already there, in this example it is placed there by the multiple image download. */ + txm_module_manager_in_place_load(&my_module, "my module", (VOID *) MODULE_CODE); + + /* Enable 128 byte read/write shared memory region. */ + txm_module_manager_external_memory_enable(&my_module, (void *) EXTERNAL_MEMORY, EXTERNAL_MEMORY_SIZE, TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE); + + /* Start the module. */ + txm_module_manager_start(&my_module); + + /* Sleep for a while.... */ + tx_thread_sleep(1000); + + /* Stop the module. */ + txm_module_manager_stop(&my_module); + + /* Unload the module. */ + txm_module_manager_unload(&my_module); + + /* Load the module that is already there. */ + txm_module_manager_in_place_load(&my_module, "my module", (VOID *) MODULE_CODE); + + /* Start the module again. */ + txm_module_manager_start(&my_module); + + /* Now just spin... */ + while(1) + { + + tx_thread_sleep(100); + } +} diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewd b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewd new file mode 100644 index 00000000..a384c409 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewd @@ -0,0 +1,2974 @@ + + + 3 + + Debug + + ARM + + 1 + + C-SPY + 2 + + 32 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ARMSIM_ID + 2 + + 1 + 1 + 1 + + + + + + + + CADI_ID + 2 + + 0 + 1 + 1 + + + + + + + + + CMSISDAP_ID + 2 + + 4 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GDBSERVER_ID + 2 + + 0 + 1 + 1 + + + + + + + + + + + IJET_ID + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JLINK_ID + 2 + + 16 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LMIFTDI_ID + 2 + + 2 + 1 + 1 + + + + + + + + + + NULINK_ID + 2 + + 0 + 1 + 1 + + + + + + + PEMICRO_ID + 2 + + 3 + 1 + 1 + + + + + + + + STLINK_ID + 2 + + 7 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + THIRDPARTY_ID + 2 + + 0 + 1 + 1 + + + + + + + + TIFET_ID + 2 + + 1 + 1 + 1 + + + + + + + + + + + + + + + + + + + XDS100_ID + 2 + + 8 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\FreeRtos\FreeRtosArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\HWRTOSplugin\HWRTOSplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin2.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\RemedyRtosViewer\RemedyRtosViewer.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8b.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8bBE.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\ThreadX\ThreadXArmPlugin.ENU.ewplugin + 1 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\TargetAccessServer\TargetAccessServer.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + + Release + + ARM + + 0 + + C-SPY + 2 + + 32 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ARMSIM_ID + 2 + + 1 + 1 + 0 + + + + + + + + CADI_ID + 2 + + 0 + 1 + 0 + + + + + + + + + CMSISDAP_ID + 2 + + 4 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GDBSERVER_ID + 2 + + 0 + 1 + 0 + + + + + + + + + + + IJET_ID + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + JLINK_ID + 2 + + 16 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LMIFTDI_ID + 2 + + 2 + 1 + 0 + + + + + + + + + + NULINK_ID + 2 + + 0 + 1 + 0 + + + + + + + PEMICRO_ID + 2 + + 3 + 1 + 0 + + + + + + + + STLINK_ID + 2 + + 7 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + THIRDPARTY_ID + 2 + + 0 + 1 + 0 + + + + + + + + TIFET_ID + 2 + + 1 + 1 + 0 + + + + + + + + + + + + + + + + + + + XDS100_ID + 2 + + 8 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\CMX\CmxTinyArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\embOS\embOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\FreeRtos\FreeRtosArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\HWRTOSplugin\HWRTOSplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\Mbed\MbedArmPlugin2.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\OpenRTOS\OpenRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\RemedyRtosViewer\RemedyRtosViewer.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SafeRTOS\SafeRTOSPlugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8b.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\SMX\smxAwareIarArm8bBE.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\ThreadX\ThreadXArmPlugin.ENU.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\TI-RTOS\tirtosplugin.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-286-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-II\uCOS-II-KA-CSpy.ewplugin + 0 + + + $TOOLKIT_DIR$\plugins\rtos\uCOS-III\uCOS-III-KA-CSpy.ewplugin + 0 + + + $EW_DIR$\common\plugins\Orti\Orti.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\TargetAccessServer\TargetAccessServer.ENU.ewplugin + 0 + + + $EW_DIR$\common\plugins\uCProbe\uCProbePlugin.ENU.ewplugin + 0 + + + + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewp b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewp new file mode 100644 index 00000000..546bbc38 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.ewp @@ -0,0 +1,2140 @@ + + + 3 + + Debug + + ARM + + 1 + + General + 3 + + 31 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 31 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + $PROJ_DIR$\sample_threadx_module_manager.c + + + $PROJ_DIR$\startup.s + + + $PROJ_DIR$\Debug\Exe\tx.a + + + $PROJ_DIR$\tx_initialize_low_level.s + + diff --git a/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.icf b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.icf new file mode 100644 index 00000000..5e6f652e --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/sample_threadx_module_manager.icf @@ -0,0 +1,36 @@ +/*###ICF### Section handled by ICF editor, don't touch! ****/ +/*-Editor annotation file-*/ +/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */ +/*-Specials-*/ +define symbol __ICFEDIT_intvec_start__ = 0x08000000; +/*-Memory Regions-*/ +define symbol __ICFEDIT_region_ROM_start__ = 0x08000000; +define symbol __ICFEDIT_region_ROM_end__ = 0x080FFFFF; +define symbol __ICFEDIT_region_RAM_start__ = 0x64000000; +define symbol __ICFEDIT_region_RAM_end__ = 0x640FFFFF; +define symbol __ICFEDIT_region_IRAM_start__ = 0x20000000; +define symbol __ICFEDIT_region_IRAM_end__ = 0x2001FFFF; +/*-Sizes-*/ +define symbol __ICFEDIT_size_cstack__ = 0x400; +define symbol __ICFEDIT_size_heap__ = 0x200; +/**** End of ICF editor section. ###ICF###*/ + + +define memory mem with size = 4G; +define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__]; +define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__]; +define region IRAM_region = mem:[from __ICFEDIT_region_IRAM_start__ to __ICFEDIT_region_IRAM_end__]; + +define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { }; +define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { }; + +initialize by copy { readwrite }; +do not initialize { section .noinit }; + +place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec }; + +place in ROM_region { readonly }; +place in IRAM_region { block HEAP }; +place in IRAM_region { readwrite, block CSTACK }; + +place in IRAM_region { last section FREE_MEM}; diff --git a/ports_module/cortex_m0+/iar/example_build/settings/sample_threadx_module_manager.dnx b/ports_module/cortex_m0+/iar/example_build/settings/sample_threadx_module_manager.dnx new file mode 100644 index 00000000..071890fe --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/settings/sample_threadx_module_manager.dnx @@ -0,0 +1,105 @@ + + + + 0 + 1 + 90 + 1 + 1 + 1 + main + 0 + 50 + + + 0 + 1 + + + 2230901326 + + + 1 + 0 + + + 0 + 0 + 0 + + + _ 0 + _ 0 + + + 0 + + + TXM_MODULE_INSTANCE-txm_module_instance_id 4 0 + + + 0 + 1 + 0 + 0 + + + 0 + + + 1 + + + _ 0 + _ "" + + + _ 0 + _ "" + _ 0 + + + 0 + 0 + 1 + 0 + 1 + 0 + + + 0 + 0 + 1 + 0 + 1 + + + 0 + + + 0 + + + 1 + + + 1 + 1 + 0 + 0 + 1 + _ 0 135200768 135266303 1 + _ 0 536870912 537001983 0 + _ 0 1677721600 1678770175 0 + 3 + + + 0 + 0 + + + 10000000 + 0 + 1 + + diff --git a/ports_module/cortex_m0+/iar/example_build/startup.s b/ports_module/cortex_m0+/iar/example_build/startup.s new file mode 100644 index 00000000..bbe8142b --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/startup.s @@ -0,0 +1,622 @@ +;/******************** (C) COPYRIGHT 2011 STMicroelectronics ******************** +;* File Name : startup.s +;* Author : MCD Application Team +;* Version : V1.0.0 +;* Date : 18-April-2011 +;* Description : STM32F2xx devices vector table for EWARM toolchain. +;* This module performs: +;* - Set the initial SP +;* - Configure he external SRAM mounted on STM322xG-EVAL board +;* to be used as data memory (optional, to be enabled by user) +;* - Set the initial PC == __iar_program_start, +;* - Set the vector table entries with the exceptions ISR +;* address. +;* After Reset the Cortex-M3 processor is in Thread mode, +;* priority is Privileged, and the Stack is set to Main. +;******************************************************************************** +;* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS +;* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME. +;* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, +;* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE +;* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING +;* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. +;*******************************************************************************/ +; +; +; The modules in this file are included in the libraries, and may be replaced +; by any user-defined modules that define the PUBLIC symbol _program_start or +; a user defined start symbol. +; To override the cstartup defined in the library, simply add your modified +; version to the workbench project. +; +; The vector table is normally located at address 0. +; When debugging in RAM, it can be located in RAM, aligned to at least 2^6. +; The name "__vector_table" has special meaning for C-SPY: +; it is where the SP start value is found, and the NVIC vector +; table register (VTOR) is initialized to this address if != 0. +; +; Cortex-M version +; + +__initial_spTop EQU 0x20000400 ; stack used for SystemInit & SystemInit_ExtMemCtl + + MODULE ?cstartup + + ;; Forward declaration of sections. + SECTION CSTACK:DATA:NOROOT(3) + + SECTION .intvec:CODE:NOROOT(2) + + EXTERN __iar_program_start + EXTERN SystemInit + PUBLIC __vector_table + + DATA +__vector_table + DCD __initial_spTop ; Use internal RAM for stack for calling SystemInit + DCD Reset_Handler ; Reset Handler + + DC32 NMI_Handler ; NMI + DC32 HardFault_Handler ; HardFault + DC32 MemManage_Handler ; MemManage + DC32 0 ; BusFault + DC32 0 ; UsageFault + DC32 0 ; 7 + DC32 0 ; 8 + DC32 0 ; 9 + DC32 0 ; 10 + DC32 SVC_Handler ; SVCall + DC32 DebugMon_Handler ; Monitor + DC32 0 ; 13 + DC32 PendSV_Handler ; PendSV + DC32 SysTick_Handler ; SysTick + + ; External Interrupts + DCD WWDG_IRQHandler ; Window WatchDog + DCD PVD_IRQHandler ; PVD through EXTI Line detection + DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line + DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line + DCD FLASH_IRQHandler ; FLASH + DCD RCC_IRQHandler ; RCC + DCD EXTI0_IRQHandler ; EXTI Line0 + DCD EXTI1_IRQHandler ; EXTI Line1 + DCD EXTI2_IRQHandler ; EXTI Line2 + DCD EXTI3_IRQHandler ; EXTI Line3 + DCD EXTI4_IRQHandler ; EXTI Line4 + DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0 + DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1 + DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2 + DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3 + DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4 + DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5 + DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6 + DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s + DCD CAN1_TX_IRQHandler ; CAN1 TX + DCD CAN1_RX0_IRQHandler ; CAN1 RX0 + DCD CAN1_RX1_IRQHandler ; CAN1 RX1 + DCD CAN1_SCE_IRQHandler ; CAN1 SCE + DCD EXTI9_5_IRQHandler ; External Line[9:5]s + DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9 + DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10 + DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11 + DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare + DCD TIM2_IRQHandler ; TIM2 + DCD TIM3_IRQHandler ; TIM3 + DCD TIM4_IRQHandler ; TIM4 + DCD I2C1_EV_IRQHandler ; I2C1 Event + DCD I2C1_ER_IRQHandler ; I2C1 Error + DCD I2C2_EV_IRQHandler ; I2C2 Event + DCD I2C2_ER_IRQHandler ; I2C2 Error + DCD SPI1_IRQHandler ; SPI1 + DCD SPI2_IRQHandler ; SPI2 + DCD USART1_IRQHandler ; USART1 + DCD USART2_IRQHandler ; USART2 + DCD USART3_IRQHandler ; USART3 + DCD EXTI15_10_IRQHandler ; External Line[15:10]s + DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line + DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line + DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12 + DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13 + DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14 + DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare + DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7 + DCD FSMC_IRQHandler ; FSMC + DCD SDIO_IRQHandler ; SDIO + DCD TIM5_IRQHandler ; TIM5 + DCD SPI3_IRQHandler ; SPI3 + DCD UART4_IRQHandler ; UART4 + DCD UART5_IRQHandler ; UART5 + DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors + DCD TIM7_IRQHandler ; TIM7 + DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0 + DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1 + DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2 + DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3 + DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4 + DCD ETH_IRQHandler ; Ethernet + DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line + DCD CAN2_TX_IRQHandler ; CAN2 TX + DCD CAN2_RX0_IRQHandler ; CAN2 RX0 + DCD CAN2_RX1_IRQHandler ; CAN2 RX1 + DCD CAN2_SCE_IRQHandler ; CAN2 SCE + DCD OTG_FS_IRQHandler ; USB OTG FS + DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5 + DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6 + DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7 + DCD USART6_IRQHandler ; USART6 + DCD I2C3_EV_IRQHandler ; I2C3 event + DCD I2C3_ER_IRQHandler ; I2C3 error + DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out + DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In + DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI + DCD OTG_HS_IRQHandler ; USB OTG HS + DCD DCMI_IRQHandler ; DCMI + DCD CRYP_IRQHandler ; CRYP crypto + DCD HASH_RNG_IRQHandler ; Hash and Rng + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Default interrupt handlers. +;; + THUMB + PUBWEAK Reset_Handler + SECTION .text:CODE:NOROOT(2) +Reset_Handler + CPSID i ; Disable interrupts + LDR R0, =sfe(CSTACK) ; restore original stack pointer + MSR MSP, R0 + LDR R0, =__iar_program_start ; Jump to ThreadX start, which will call IAR startup code + BX R0 + + PUBWEAK NMI_Handler + SECTION .text:CODE:NOROOT(2) +NMI_Handler + B NMI_Handler + + PUBWEAK HardFault_Handler + SECTION .text:CODE:NOROOT(2) +HardFault_Handler + B HardFault_Handler + + PUBWEAK MemManage_Handler + SECTION .text:CODE:NOROOT(2) +MemManage_Handler + B MemManage_Handler + + PUBWEAK BusFault_Handler + SECTION .text:CODE:NOROOT(2) +BusFault_Handler + B BusFault_Handler + + PUBWEAK UsageFault_Handler + SECTION .text:CODE:NOROOT(2) +UsageFault_Handler + B UsageFault_Handler + + PUBWEAK SVC_Handler + SECTION .text:CODE:NOROOT(2) +SVC_Handler + B SVC_Handler + + PUBWEAK DebugMon_Handler + SECTION .text:CODE:NOROOT(2) +DebugMon_Handler + B DebugMon_Handler + + PUBWEAK PendSV_Handler + SECTION .text:CODE:NOROOT(2) +PendSV_Handler + B PendSV_Handler + + PUBWEAK SysTick_Handler + SECTION .text:CODE:NOROOT(2) +SysTick_Handler + B SysTick_Handler + + PUBWEAK WWDG_IRQHandler + SECTION .text:CODE:NOROOT(2) +WWDG_IRQHandler + B WWDG_IRQHandler + + PUBWEAK PVD_IRQHandler + SECTION .text:CODE:NOROOT(2) +PVD_IRQHandler + B PVD_IRQHandler + + PUBWEAK TAMP_STAMP_IRQHandler + SECTION .text:CODE:NOROOT(2) +TAMP_STAMP_IRQHandler + B TAMP_STAMP_IRQHandler + + PUBWEAK RTC_WKUP_IRQHandler + SECTION .text:CODE:NOROOT(2) +RTC_WKUP_IRQHandler + B RTC_WKUP_IRQHandler + + PUBWEAK FLASH_IRQHandler + SECTION .text:CODE:NOROOT(2) +FLASH_IRQHandler + B FLASH_IRQHandler + + PUBWEAK RCC_IRQHandler + SECTION .text:CODE:NOROOT(2) +RCC_IRQHandler + B RCC_IRQHandler + + PUBWEAK EXTI0_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI0_IRQHandler + B EXTI0_IRQHandler + + PUBWEAK EXTI1_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI1_IRQHandler + B EXTI1_IRQHandler + + PUBWEAK EXTI2_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI2_IRQHandler + B EXTI2_IRQHandler + + PUBWEAK EXTI3_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI3_IRQHandler + B EXTI3_IRQHandler + + PUBWEAK EXTI4_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI4_IRQHandler + B EXTI4_IRQHandler + + PUBWEAK DMA1_Stream0_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream0_IRQHandler + B DMA1_Stream0_IRQHandler + + PUBWEAK DMA1_Stream1_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream1_IRQHandler + B DMA1_Stream1_IRQHandler + + PUBWEAK DMA1_Stream2_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream2_IRQHandler + B DMA1_Stream2_IRQHandler + + PUBWEAK DMA1_Stream3_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream3_IRQHandler + B DMA1_Stream3_IRQHandler + + PUBWEAK DMA1_Stream4_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream4_IRQHandler + B DMA1_Stream4_IRQHandler + + PUBWEAK DMA1_Stream5_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream5_IRQHandler + B DMA1_Stream5_IRQHandler + + PUBWEAK DMA1_Stream6_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream6_IRQHandler + B DMA1_Stream6_IRQHandler + + PUBWEAK ADC_IRQHandler + SECTION .text:CODE:NOROOT(2) +ADC_IRQHandler + B ADC_IRQHandler + + PUBWEAK CAN1_TX_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN1_TX_IRQHandler + B CAN1_TX_IRQHandler + + PUBWEAK CAN1_RX0_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN1_RX0_IRQHandler + B CAN1_RX0_IRQHandler + + PUBWEAK CAN1_RX1_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN1_RX1_IRQHandler + B CAN1_RX1_IRQHandler + + PUBWEAK CAN1_SCE_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN1_SCE_IRQHandler + B CAN1_SCE_IRQHandler + + PUBWEAK EXTI9_5_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI9_5_IRQHandler + B EXTI9_5_IRQHandler + + PUBWEAK TIM1_BRK_TIM9_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM1_BRK_TIM9_IRQHandler + B TIM1_BRK_TIM9_IRQHandler + + PUBWEAK TIM1_UP_TIM10_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM1_UP_TIM10_IRQHandler + B TIM1_UP_TIM10_IRQHandler + + PUBWEAK TIM1_TRG_COM_TIM11_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM1_TRG_COM_TIM11_IRQHandler + B TIM1_TRG_COM_TIM11_IRQHandler + + PUBWEAK TIM1_CC_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM1_CC_IRQHandler + B TIM1_CC_IRQHandler + + PUBWEAK TIM2_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM2_IRQHandler + B TIM2_IRQHandler + + PUBWEAK TIM3_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM3_IRQHandler + B TIM3_IRQHandler + + PUBWEAK TIM4_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM4_IRQHandler + B TIM4_IRQHandler + + PUBWEAK I2C1_EV_IRQHandler + SECTION .text:CODE:NOROOT(2) +I2C1_EV_IRQHandler + B I2C1_EV_IRQHandler + + PUBWEAK I2C1_ER_IRQHandler + SECTION .text:CODE:NOROOT(2) +I2C1_ER_IRQHandler + B I2C1_ER_IRQHandler + + PUBWEAK I2C2_EV_IRQHandler + SECTION .text:CODE:NOROOT(2) +I2C2_EV_IRQHandler + B I2C2_EV_IRQHandler + + PUBWEAK I2C2_ER_IRQHandler + SECTION .text:CODE:NOROOT(2) +I2C2_ER_IRQHandler + B I2C2_ER_IRQHandler + + PUBWEAK SPI1_IRQHandler + SECTION .text:CODE:NOROOT(2) +SPI1_IRQHandler + B SPI1_IRQHandler + + PUBWEAK SPI2_IRQHandler + SECTION .text:CODE:NOROOT(2) +SPI2_IRQHandler + B SPI2_IRQHandler + + PUBWEAK USART1_IRQHandler + SECTION .text:CODE:NOROOT(2) +USART1_IRQHandler + B USART1_IRQHandler + + PUBWEAK USART2_IRQHandler + SECTION .text:CODE:NOROOT(2) +USART2_IRQHandler + B USART2_IRQHandler + + PUBWEAK USART3_IRQHandler + SECTION .text:CODE:NOROOT(2) +USART3_IRQHandler + B USART3_IRQHandler + + PUBWEAK EXTI15_10_IRQHandler + SECTION .text:CODE:NOROOT(2) +EXTI15_10_IRQHandler + B EXTI15_10_IRQHandler + + PUBWEAK RTC_Alarm_IRQHandler + SECTION .text:CODE:NOROOT(2) +RTC_Alarm_IRQHandler + B RTC_Alarm_IRQHandler + + PUBWEAK OTG_FS_WKUP_IRQHandler + SECTION .text:CODE:NOROOT(2) +OTG_FS_WKUP_IRQHandler + B OTG_FS_WKUP_IRQHandler + + PUBWEAK TIM8_BRK_TIM12_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM8_BRK_TIM12_IRQHandler + B TIM8_BRK_TIM12_IRQHandler + + PUBWEAK TIM8_UP_TIM13_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM8_UP_TIM13_IRQHandler + B TIM8_UP_TIM13_IRQHandler + + PUBWEAK TIM8_TRG_COM_TIM14_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM8_TRG_COM_TIM14_IRQHandler + B TIM8_TRG_COM_TIM14_IRQHandler + + PUBWEAK TIM8_CC_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM8_CC_IRQHandler + B TIM8_CC_IRQHandler + + PUBWEAK DMA1_Stream7_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA1_Stream7_IRQHandler + B DMA1_Stream7_IRQHandler + + PUBWEAK FSMC_IRQHandler + SECTION .text:CODE:NOROOT(2) +FSMC_IRQHandler + B FSMC_IRQHandler + + PUBWEAK SDIO_IRQHandler + SECTION .text:CODE:NOROOT(2) +SDIO_IRQHandler + B SDIO_IRQHandler + + PUBWEAK TIM5_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM5_IRQHandler + B TIM5_IRQHandler + + PUBWEAK SPI3_IRQHandler + SECTION .text:CODE:NOROOT(2) +SPI3_IRQHandler + B SPI3_IRQHandler + + PUBWEAK UART4_IRQHandler + SECTION .text:CODE:NOROOT(2) +UART4_IRQHandler + B UART4_IRQHandler + + PUBWEAK UART5_IRQHandler + SECTION .text:CODE:NOROOT(2) +UART5_IRQHandler + B UART5_IRQHandler + + PUBWEAK TIM6_DAC_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM6_DAC_IRQHandler + B TIM6_DAC_IRQHandler + + PUBWEAK TIM7_IRQHandler + SECTION .text:CODE:NOROOT(2) +TIM7_IRQHandler + B TIM7_IRQHandler + + PUBWEAK DMA2_Stream0_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream0_IRQHandler + B DMA2_Stream0_IRQHandler + + PUBWEAK DMA2_Stream1_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream1_IRQHandler + B DMA2_Stream1_IRQHandler + + PUBWEAK DMA2_Stream2_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream2_IRQHandler + B DMA2_Stream2_IRQHandler + + PUBWEAK DMA2_Stream3_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream3_IRQHandler + B DMA2_Stream3_IRQHandler + + PUBWEAK DMA2_Stream4_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream4_IRQHandler + B DMA2_Stream4_IRQHandler + + PUBWEAK ETH_IRQHandler + SECTION .text:CODE:NOROOT(2) +ETH_IRQHandler + B ETH_IRQHandler + + PUBWEAK ETH_WKUP_IRQHandler + SECTION .text:CODE:NOROOT(2) +ETH_WKUP_IRQHandler + B ETH_WKUP_IRQHandler + + PUBWEAK CAN2_TX_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN2_TX_IRQHandler + B CAN2_TX_IRQHandler + + PUBWEAK CAN2_RX0_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN2_RX0_IRQHandler + B CAN2_RX0_IRQHandler + + PUBWEAK CAN2_RX1_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN2_RX1_IRQHandler + B CAN2_RX1_IRQHandler + + PUBWEAK CAN2_SCE_IRQHandler + SECTION .text:CODE:NOROOT(2) +CAN2_SCE_IRQHandler + B CAN2_SCE_IRQHandler + + PUBWEAK OTG_FS_IRQHandler + SECTION .text:CODE:NOROOT(2) +OTG_FS_IRQHandler + B OTG_FS_IRQHandler + + PUBWEAK DMA2_Stream5_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream5_IRQHandler + B DMA2_Stream5_IRQHandler + + PUBWEAK DMA2_Stream6_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream6_IRQHandler + B DMA2_Stream6_IRQHandler + + PUBWEAK DMA2_Stream7_IRQHandler + SECTION .text:CODE:NOROOT(2) +DMA2_Stream7_IRQHandler + B DMA2_Stream7_IRQHandler + + PUBWEAK USART6_IRQHandler + SECTION .text:CODE:NOROOT(2) +USART6_IRQHandler + B USART6_IRQHandler + + PUBWEAK I2C3_EV_IRQHandler + SECTION .text:CODE:NOROOT(2) +I2C3_EV_IRQHandler + B I2C3_EV_IRQHandler + + PUBWEAK I2C3_ER_IRQHandler + SECTION .text:CODE:NOROOT(2) +I2C3_ER_IRQHandler + B I2C3_ER_IRQHandler + + PUBWEAK OTG_HS_EP1_OUT_IRQHandler + SECTION .text:CODE:NOROOT(2) +OTG_HS_EP1_OUT_IRQHandler + B OTG_HS_EP1_OUT_IRQHandler + + PUBWEAK OTG_HS_EP1_IN_IRQHandler + SECTION .text:CODE:NOROOT(2) +OTG_HS_EP1_IN_IRQHandler + B OTG_HS_EP1_IN_IRQHandler + + PUBWEAK OTG_HS_WKUP_IRQHandler + SECTION .text:CODE:NOROOT(2) +OTG_HS_WKUP_IRQHandler + B OTG_HS_WKUP_IRQHandler + + PUBWEAK OTG_HS_IRQHandler + SECTION .text:CODE:NOROOT(2) +OTG_HS_IRQHandler + B OTG_HS_IRQHandler + + PUBWEAK DCMI_IRQHandler + SECTION .text:CODE:NOROOT(2) +DCMI_IRQHandler + B DCMI_IRQHandler + + PUBWEAK CRYP_IRQHandler + SECTION .text:CODE:NOROOT(2) +CRYP_IRQHandler + B CRYP_IRQHandler + + PUBWEAK HASH_RNG_IRQHandler + SECTION .text:CODE:NOROOT(2) +HASH_RNG_IRQHandler + B HASH_RNG_IRQHandler + + END +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/ports_module/cortex_m0+/iar/example_build/tx.ewp b/ports_module/cortex_m0+/iar/example_build/tx.ewp new file mode 100644 index 00000000..6048c0c7 --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/tx.ewp @@ -0,0 +1,2867 @@ + + + 3 + + Debug + + ARM + + 1 + + General + 3 + + 31 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 31 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + inc + + $PROJ_DIR$\..\..\..\..\common\inc\tx_api.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_block_pool.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_byte_pool.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_event_flags.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_initialize.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_mutex.h + + + $PROJ_DIR$\..\inc\tx_port.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_queue.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_semaphore.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_thread.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_timer.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_trace.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_user_sample.h + + + $PROJ_DIR$\..\..\..\..\common_modules\inc\txm_module.h + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\inc\txm_module_manager_dispatch.h + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\inc\txm_module_manager_util.h + + + $PROJ_DIR$\..\inc\txm_module_port.h + + + $PROJ_DIR$\..\..\..\..\common_modules\inc\txm_module_user.h + + + + src + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_allocate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_cleanup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_pool_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_block_release.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_allocate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_cleanup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_pool_search.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_byte_release.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_cleanup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_set.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_event_flags_set_notify.c + + + $PROJ_DIR$\..\module_manager\src\tx_iar.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_initialize_high_level.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_initialize_kernel_enter.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_initialize_kernel_setup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_cleanup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_priority_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_mutex_put.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_cleanup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_flush.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_front_send.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_receive.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_send.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_queue_send_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_ceiling_put.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_cleanup.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_put.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_semaphore_put_notify.c + + + $PROJ_DIR$\..\module_manager\src\tx_thread_context_restore.S + + + $PROJ_DIR$\..\module_manager\src\tx_thread_context_save.S + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_entry_exit_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_identify.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_initialize.c + + + $PROJ_DIR$\..\module_manager\src\tx_thread_interrupt_control.S + + + $PROJ_DIR$\..\module_manager\src\tx_thread_interrupt_disable.S + + + $PROJ_DIR$\..\module_manager\src\tx_thread_interrupt_restore.S + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_preemption_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_priority_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_relinquish.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_reset.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_resume.c + + + $PROJ_DIR$\..\module_manager\src\tx_thread_schedule.S + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_shell_entry.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_sleep.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_stack_analyze.c + + + $PROJ_DIR$\..\module_manager\src\tx_thread_stack_build.S + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_stack_error_handler.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_stack_error_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_suspend.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_system_preempt_check.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_system_resume.c + + + $PROJ_DIR$\..\module_manager\src\tx_thread_system_return.S + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_system_suspend.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_terminate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_time_slice.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_time_slice_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_timeout.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_thread_wait_abort.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_time_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_time_set.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_activate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_deactivate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_expiration_process.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_initialize.c + + + $PROJ_DIR$\..\module_manager\src\tx_timer_interrupt.S + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_system_activate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_system_deactivate.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_timer_thread_entry.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_buffer_full_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_disable.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_enable.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_event_filter.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_event_unfilter.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_initialize.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_interrupt_control.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_isr_enter_insert.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_isr_exit_insert.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_object_register.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_object_unregister.c + + + $PROJ_DIR$\..\..\..\..\common\src\tx_trace_user_event_insert.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_block_allocate.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_block_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_block_pool_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_block_pool_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_block_pool_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_block_release.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_byte_allocate.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_byte_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_byte_pool_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_byte_pool_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_byte_pool_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_byte_release.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_event_flags_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_event_flags_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_event_flags_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_event_flags_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_event_flags_set.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_event_flags_set_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_mutex_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_mutex_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_mutex_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_mutex_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_mutex_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_mutex_put.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_flush.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_front_send.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_receive.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_send.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_queue_send_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_ceiling_put.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_put.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_semaphore_put_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_entry_exit_notify.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_info_get.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_preemption_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_priority_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_relinquish.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_reset.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_resume.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_suspend.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_terminate.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_time_slice_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_thread_wait_abort.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_timer_activate.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_timer_change.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_timer_create.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_timer_deactivate.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_timer_delete.c + + + $PROJ_DIR$\..\..\..\..\common\src\txe_timer_info_get.c + + + $PROJ_DIR$\..\module_manager\src\txm_module_manager_alignment_adjust.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_application_request.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_callback_request.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_event_flags_notify_trampoline.c + + + $PROJ_DIR$\..\module_manager\src\txm_module_manager_external_memory_enable.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_file_load.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_in_place_load.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_initialize.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_internal_load.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_kernel_dispatch.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_maximum_module_priority_set.c + + + $PROJ_DIR$\..\module_manager\src\txm_module_manager_memory_fault_handler.c + + + $PROJ_DIR$\..\module_manager\src\txm_module_manager_memory_fault_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_memory_load.c + + + $PROJ_DIR$\..\module_manager\src\txm_module_manager_mm_register_setup.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_object_allocate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_object_deallocate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_object_pointer_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_object_pointer_get_extended.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_object_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_properties_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_queue_notify_trampoline.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_semaphore_notify_trampoline.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_start.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_stop.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_thread_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_thread_notify_trampoline.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_thread_reset.c + + + $PROJ_DIR$\..\module_manager\src\txm_module_manager_thread_stack_build.S + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_timer_notify_trampoline.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_unload.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_manager\src\txm_module_manager_util.c + + + diff --git a/ports_module/cortex_m0+/iar/example_build/tx_initialize_low_level.s b/ports_module/cortex_m0+/iar/example_build/tx_initialize_low_level.s new file mode 100644 index 00000000..555e7dec --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/tx_initialize_low_level.s @@ -0,0 +1,185 @@ +;/**************************************************************************/ +;/* */ +;/* Copyright (c) Microsoft Corporation. All rights reserved. */ +;/* */ +;/* This software is licensed under the Microsoft Software License */ +;/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +;/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +;/* and in the root directory of this software. */ +;/* */ +;/**************************************************************************/ +; +; +;/**************************************************************************/ +;/**************************************************************************/ +;/** */ +;/** ThreadX Component */ +;/** */ +;/** Initialize */ +;/** */ +;/**************************************************************************/ +;/**************************************************************************/ +; +;#define TX_SOURCE_CODE +; +; +;/* Include necessary system files. */ +; +;#include "tx_api.h" +;#include "tx_initialize.h" +;#include "tx_thread.h" +;#include "tx_timer.h" +; +; + EXTERN _tx_thread_system_stack_ptr + EXTERN _tx_initialize_unused_memory + EXTERN _tx_timer_interrupt + EXTERN __vector_table + EXTERN _tx_execution_isr_enter + EXTERN _tx_execution_isr_exit +; +; +SYSTEM_CLOCK EQU 7200000 +SYSTICK_CYCLES EQU ((SYSTEM_CLOCK / 100) -1) + + RSEG FREE_MEM:DATA + PUBLIC __tx_free_memory_start +__tx_free_memory_start + DS32 4 +; +; + SECTION `.text`:CODE:NOROOT(2) + THUMB +;/**************************************************************************/ +;/* */ +;/* FUNCTION RELEASE */ +;/* */ +;/* _tx_initialize_low_level Cortex-M0+/IAR */ +;/* 6.1.10 */ +;/* AUTHOR */ +;/* */ +;/* William E. Lamie, 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 */ +;/* */ +;/* RELEASE HISTORY */ +;/* */ +;/* DATE NAME DESCRIPTION */ +;/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +;/* */ +;/**************************************************************************/ +;VOID _tx_initialize_low_level(VOID) +;{ + PUBLIC _tx_initialize_low_level +_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, =__tx_free_memory_start // Build first free address + ADDS r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + + LDR r0, =0xE000ED08 // Build address of NVIC registers + LDR r1, =__vector_table // Pickup address of vector table + STR r1, [r0] // Set vector table address + + /* Set system stack pointer from vector value. */ + + LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer + LDR r1, =__vector_table // Pickup address of vector table + LDR r1, [r1] // Pickup reset stack pointer + STR r1, [r0] // Save system stack pointer + + /* Enable the cycle count register. */ + + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + MOVS r2, #1 + ORRS r1, r1, r2 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ + + LDR r0, =0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + MOVS r2, #0x14 + STR r1, [r0, r2] // Setup SysTick Reload Value + MOVS r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + LDR r2, =0xD18 + STR r1, [r0, r2] // Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + LDR r2, =0xD1C + STR r1, [r0, r2] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + LDR r2, =0xD20 + STR r1, [r0, r2] // Setup System Handlers 12-15 Priority Registers + // Note: PnSV must be lowest priority, which is 0xFF + + /* Return to caller. */ + + BX lr +// } +; +;/* Define SystTick Handler. */ +; + + PUBLIC SysTick_Handler + PUBLIC __tx_SysTickHandler +SysTick_Handler: +__tx_SysTickHandler: +; VOID SysTickHandler (VOID) +; { +; + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter ; Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit ; Call the ISR exit function +#endif + POP {r0, r1} + MOV lr, r1 + BX lr +; } + + END + + diff --git a/ports_module/cortex_m0+/iar/example_build/txm.ewp b/ports_module/cortex_m0+/iar/example_build/txm.ewp new file mode 100644 index 00000000..e13721eb --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/txm.ewp @@ -0,0 +1,2477 @@ + + + 3 + + Debug + + ARM + + 1 + + General + 3 + + 31 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 1 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 1 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + Release + + ARM + + 0 + + General + 3 + + 31 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ICCARM + 2 + + 36 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AARM + 2 + + 10 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OBJCOPY + 0 + + 1 + 1 + 0 + + + + + + + + + CUSTOM + 3 + + + + 0 + + + + BICOMP + 0 + + + + BUILDACTION + 1 + + + + + + + ILINK + 0 + + 23 + 1 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IARCHIVE + 0 + + 0 + 1 + 0 + + + + + + + BILINK + 0 + + + + Coder + 0 + + + + + inc + + $PROJ_DIR$\..\..\..\..\common\inc\tx_api.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_block_pool.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_byte_pool.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_event_flags.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_initialize.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_mutex.h + + + $PROJ_DIR$\..\module_common\inc\tx_port.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_queue.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_semaphore.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_thread.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_timer.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_trace.h + + + $PROJ_DIR$\..\..\..\..\common\inc\tx_user_sample.h + + + $PROJ_DIR$\..\..\..\..\common_modules\inc\txm_module.h + + + $PROJ_DIR$\..\inc\txm_module_port.h + + + $PROJ_DIR$\..\..\..\..\common_modules\inc\txm_module_user.h + + + + src + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_allocate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_pool_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_pool_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_pool_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_pool_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_pool_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_block_release.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_allocate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_pool_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_pool_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_pool_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_pool_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_pool_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_pool_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_byte_release.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_set.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_event_flags_set_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_application_request.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_callback_request_thread_entry.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_object_allocate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_object_deallocate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_object_pointer_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_object_pointer_get_extended.c + + + $PROJ_DIR$\..\module_lib\src\txm_module_thread_shell_entry.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_module_thread_system_suspend.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_mutex_put.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_flush.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_front_send.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_receive.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_send.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_queue_send_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_ceiling_put.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_prioritize.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_put.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_semaphore_put_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_entry_exit_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_identify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_interrupt_control.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_preemption_change.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_priority_change.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_relinquish.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_reset.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_resume.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_sleep.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_stack_error_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_suspend.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_terminate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_time_slice_change.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_thread_wait_abort.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_time_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_time_set.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_activate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_change.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_create.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_deactivate.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_delete.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_performance_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_timer_performance_system_info_get.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_buffer_full_notify.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_disable.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_enable.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_event_filter.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_event_unfilter.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_interrupt_control.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_isr_enter_insert.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_isr_exit_insert.c + + + $PROJ_DIR$\..\..\..\..\common_modules\module_lib\src\txm_trace_user_event_insert.c + + + diff --git a/ports_module/cortex_m0+/iar/example_build/txm_module_preamble.s b/ports_module/cortex_m0+/iar/example_build/txm_module_preamble.s new file mode 100644 index 00000000..5488df2a --- /dev/null +++ b/ports_module/cortex_m0+/iar/example_build/txm_module_preamble.s @@ -0,0 +1,69 @@ + SECTION .text:CODE + + AAPCS INTERWORK, ROPI, RWPI_COMPATIBLE, VFP_COMPATIBLE + PRESERVE8 + + /* Define public symbols. */ + + PUBLIC __txm_module_preamble + + + /* Define application-specific start/stop entry points for the module. */ + + EXTERN demo_module_start + + + /* Define common external refrences. */ + + EXTERN _txm_module_thread_shell_entry + EXTERN _txm_module_callback_request_thread_entry + EXTERN ROPI$$Length + EXTERN RWPI$$Length + + DATA +__txm_module_preamble: + DC32 0x4D4F4455 ; Module ID + DC32 0x6 ; Module Major Version + DC32 0x1 ; Module Minor Version + DC32 32 ; Module Preamble Size in 32-bit words + DC32 0x12345678 ; Module ID (application defined) + DC32 0x00000007 ; Module Properties where: + ; Bits 31-24: Compiler ID + ; 0 -> IAR + ; 1 -> RVDS + ; 2 -> GNU + ; Bits 23-3: Reserved + ; Bit 2: 0 -> Disable shared/external memory access + ; 1 -> Enable shared/external memory access + ; Bit 1: 0 -> No MPU protection + ; 1 -> MPU protection (must have user mode selected - bit 0 set) + ; Bit 0: 0 -> Privileged mode execution + ; 1 -> User mode execution + DC32 _txm_module_thread_shell_entry - . - 0 ; Module Shell Entry Point + DC32 demo_module_start - . - 0 ; Module Start Thread Entry Point + DC32 0 ; Module Stop Thread Entry Point + DC32 1 ; Module Start/Stop Thread Priority + DC32 1024 ; Module Start/Stop Thread Stack Size + DC32 _txm_module_callback_request_thread_entry - . - 0 ; Module Callback Thread Entry + DC32 1 ; Module Callback Thread Priority + DC32 1024 ; Module Callback Thread Stack Size + DC32 ROPI$$Length ; Module Code Size + DC32 RWPI$$Length ; Module Data Size + DC32 0 ; Reserved 0 + DC32 0 ; Reserved 1 + DC32 0 ; Reserved 2 + DC32 0 ; Reserved 3 + DC32 0 ; Reserved 4 + DC32 0 ; Reserved 5 + DC32 0 ; Reserved 6 + DC32 0 ; Reserved 7 + DC32 0 ; Reserved 8 + DC32 0 ; Reserved 9 + DC32 0 ; Reserved 10 + DC32 0 ; Reserved 11 + DC32 0 ; Reserved 12 + DC32 0 ; Reserved 13 + DC32 0 ; Reserved 14 + DC32 0 ; Reserved 15 + + END diff --git a/ports_module/cortex_m0+/iar/inc/tx_port.h b/ports_module/cortex_m0+/iar/inc/tx_port.h new file mode 100644 index 00000000..d2554f9c --- /dev/null +++ b/ports_module/cortex_m0+/iar/inc/tx_port.h @@ -0,0 +1,578 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-M0+/IAR */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include +#include +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef short SHORT; +typedef unsigned short USHORT; + + +/* Define the TX_MEMSET macro to remove library reference. */ +/* +#define TX_MEMSET(a,b,c) { \ + UCHAR *ptr; \ + UCHAR value; \ + UINT i, size; \ + ptr = (UCHAR *) ((VOID *) a); \ + value = (UCHAR) b; \ + size = (UINT) c; \ + for (i = 0; i < size; i++) \ + { \ + *ptr++ = value; \ + } \ + } +*/ +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX Cortex-M0+ port. */ + +#define TX_INT_DISABLE 1 /* Disable interrupts */ +#define TX_INT_ENABLE 0 /* Enable interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0xE0001004) +#endif +#else +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif + +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (0) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_iar_tls_pointer; +#else +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; +#endif +#ifndef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#define TX_THREAD_EXTENSION_3 +#else +#define TX_THREAD_EXTENSION_3 unsigned long long tx_thread_execution_time_total; \ + unsigned long long tx_thread_execution_time_last_start; +#endif + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION VOID *tx_event_flags_group_module_instance; \ + VOID (*tx_event_flags_group_set_module_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *group_ptr); + +#define TX_QUEUE_EXTENSION VOID *tx_queue_module_instance; \ + VOID (*tx_queue_send_module_notify)(struct TX_QUEUE_STRUCT *queue_ptr); + +#define TX_SEMAPHORE_EXTENSION VOID *tx_semaphore_module_instance; \ + VOID (*tx_semaphore_put_module_notify)(struct TX_SEMAPHORE_STRUCT *semaphore_ptr); + +#define TX_TIMER_EXTENSION VOID *tx_timer_module_instance; \ + VOID (*tx_timer_module_expiration_function)(ULONG id); + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#if (__VER__ < 8000000) +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = __iar_dlib_perthread_allocate(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) __iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION __iar_dlib_perthread_access(0); +#else +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); +#endif +#else +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#endif + +#ifdef TX_ENABLE_FPU_SUPPORT + + +#ifdef TX_MISRA_ENABLE + +ULONG _tx_misra_control_get(void); +void _tx_misra_control_set(ULONG value); +ULONG _tx_misra_fpccr_get(void); +void _tx_misra_vfp_touch(void); + +#else + +__attribute__( ( always_inline ) ) static inline ULONG __get_control(void) +{ + +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); +} + + +__attribute__( ( always_inline ) ) static inline void __set_control(ULONG control_value) +{ + + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); +} + + +#endif + + +/* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA + in order to ensure no lazy stacking will occur. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } + +#endif + + +/* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. + If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + the lazy FPU save, then restore the CONTROL.FPCA state. */ + +#ifndef TX_MISRA_ENABLE + + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + __asm__ volatile ("vmov.f32 s0, s0"); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = __get_control(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control(_tx_vfp_state); \ + } \ + } \ + } \ + } +#else + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } +#endif + +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +#endif + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define the get system state macro. */ + +#ifndef TX_THREAD_GET_SYSTEM_STATE +#ifndef TX_MISRA_ENABLE +/* +__attribute__( ( always_inline ) ) static inline unsigned int __get_ipsr_value(void) +{ + +unsigned int ipsr_value; + + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} + + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_ipsr_value()) +*/ +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) +#else +ULONG _tx_misra_ipsr_get(VOID); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) +#endif +#endif + + +/* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value + indicates that _tx_thread_system_return should not be called. This overrides the definition in tx_thread.h + for Cortex-M since so we don't waste time checking the _tx_thread_system_state variable that is always + zero after initialization for Cortex-M ports. */ + +#ifndef TX_THREAD_SYSTEM_RETURN_CHECK +#define TX_THREAD_SYSTEM_RETURN_CHECK(c) (c) = ((ULONG) _tx_thread_preempt_disable); +#endif + + +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to + prevent early scheduling on Cortex-M parts. */ + +#define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; + + +/* This ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) { (b) = 0; \ + (m) = (m) & (-(m)); \ + while ((m) >>= 1) \ + { \ + (b)++; \ + } \ + } + + +#endif + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#ifdef TX_DISABLE_INLINE + +UINT _tx_thread_interrupt_disable(VOID); +VOID _tx_thread_interrupt_restore(UINT previous_posture); + +#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); + +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); + +#else + +#define TX_INTERRUPT_SAVE_AREA __istate_t interrupt_save; +#define TX_DISABLE {interrupt_save = __get_interrupt_state();__disable_interrupt();}; +#define TX_RESTORE {__set_interrupt_state(interrupt_save);}; + +#define _tx_thread_system_return _tx_thread_system_return_inline + +static void _tx_thread_system_return_inline(void) +{ +__istate_t interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (__get_IPSR() == 0) + { + interrupt_save = __get_interrupt_state(); + __enable_interrupt(); + __set_interrupt_state(interrupt_save); + } +} + +#endif + + +/* Define FPU extension for the Cortex-M7. Each is assumed to be called in the context of the executing + thread. These are no longer needed, but are preserved for backward compatibility only. */ + +void tx_thread_fpu_enable(void); +void tx_thread_fpu_disable(void); + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M0+/IAR Version 6.1.10 *"; +#else +#ifdef TX_MISRA_ENABLE +extern CHAR _tx_version_id[100]; +#else +extern CHAR _tx_version_id[]; +#endif +#endif + + +#endif diff --git a/ports_module/cortex_m0+/iar/inc/txm_module_port.h b/ports_module/cortex_m0+/iar/inc/txm_module_port.h new file mode 100644 index 00000000..51253ef0 --- /dev/null +++ b/ports_module/cortex_m0+/iar/inc/txm_module_port.h @@ -0,0 +1,381 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* txm_module_port.h Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the basic module constants, interface structures, */ +/* and function prototypes. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TXM_MODULE_PORT_H +#define TXM_MODULE_PORT_H + +/* Determine if the optional Modules user define file should be used. */ + +#ifdef TXM_MODULE_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in txm_module_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "txm_module_user.h" +#endif + +/* It is assumed that the base ThreadX tx_port.h file has been modified to add the + following extensions to the ThreadX thread control block (this code should replace + the corresponding macro define in tx_port.h): + +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; + +The following extensions must also be defined in tx_port.h: + +#define TX_EVENT_FLAGS_GROUP_EXTENSION VOID *tx_event_flags_group_module_instance; \ + VOID (*tx_event_flags_group_set_module_notify)(struct TX_EVENT_FLAGS_GROUP_STRUCT *group_ptr); + +#define TX_QUEUE_EXTENSION VOID *tx_queue_module_instance; \ + VOID (*tx_queue_send_module_notify)(struct TX_QUEUE_STRUCT *queue_ptr); + +#define TX_SEMAPHORE_EXTENSION VOID *tx_semaphore_module_instance; \ + VOID (*tx_semaphore_put_module_notify)(struct TX_SEMAPHORE_STRUCT *semaphore_ptr); + +#define TX_TIMER_EXTENSION VOID *tx_timer_module_instance; \ + VOID (*tx_timer_module_expiration_function)(ULONG id); +*/ + +/* Define the kernel stack size for a module thread. */ +#ifndef TXM_MODULE_KERNEL_STACK_SIZE +#define TXM_MODULE_KERNEL_STACK_SIZE 768 +#endif + +/* For the following 3 access control settings, change TEX and C, B, S (bits 21 through 16 of MPU_RASR) + * to reflect your system memory attributes (cache, shareable, memory type). */ +/* Code region access control: privileged read-only, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_CODE_ACCESS_CONTROL +#define TXM_MODULE_MPU_CODE_ACCESS_CONTROL 0x06070000 +#endif +/* Data region access control: execute never, read/write, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_DATA_ACCESS_CONTROL +#define TXM_MODULE_MPU_DATA_ACCESS_CONTROL 0x13070000 +#endif +/* Shared region access control: execute never, read-only, outer & inner write-back, normal memory, shareable. */ +#ifndef TXM_MODULE_MPU_SHARED_ACCESS_CONTROL +#define TXM_MODULE_MPU_SHARED_ACCESS_CONTROL 0x12070000 +#endif + +/* Define constants specific to the tools the module can be built with for this particular modules port. */ + +#define TXM_MODULE_IAR_COMPILER 0x00000000 +#define TXM_MODULE_RVDS_COMPILER 0x01000000 +#define TXM_MODULE_GNU_COMPILER 0x02000000 +#define TXM_MODULE_COMPILER_MASK 0xFF000000 +#define TXM_MODULE_OPTIONS_MASK 0x000000FF + + +/* Define the properties for this particular module port. */ + +#define TXM_MODULE_MEMORY_PROTECTION_ENABLED + +#ifdef TXM_MODULE_MEMORY_PROTECTION_ENABLED +#define TXM_MODULE_REQUIRE_ALLOCATED_OBJECT_MEMORY +#else +#define TXM_MODULE_REQUIRE_LOCAL_OBJECT_MEMORY +#endif + +#define TXM_MODULE_USER_MODE 0x00000001 +#define TXM_MODULE_MEMORY_PROTECTION 0x00000002 +#define TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS 0x00000004 + + +/* Define the supported options for this module. */ + +#define TXM_MODULE_MANAGER_SUPPORTED_OPTIONS (TXM_MODULE_USER_MODE | TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS) +#define TXM_MODULE_MANAGER_REQUIRED_OPTIONS 0 + + +/* Define offset adjustments according to the compiler used to build the module. */ + +#define TXM_MODULE_IAR_SHELL_ADJUST 24 +#define TXM_MODULE_IAR_START_ADJUST 28 +#define TXM_MODULE_IAR_STOP_ADJUST 32 +#define TXM_MODULE_IAR_CALLBACK_ADJUST 44 + +#define TXM_MODULE_RVDS_SHELL_ADJUST 0 +#define TXM_MODULE_RVDS_START_ADJUST 0 +#define TXM_MODULE_RVDS_STOP_ADJUST 0 +#define TXM_MODULE_RVDS_CALLBACK_ADJUST 0 + +#define TXM_MODULE_GNU_SHELL_ADJUST 24 +#define TXM_MODULE_GNU_START_ADJUST 28 +#define TXM_MODULE_GNU_STOP_ADJUST 32 +#define TXM_MODULE_GNU_CALLBACK_ADJUST 44 + + +/* Define other module port-specific constants. */ + +/* Define INLINE_DECLARE to inline for ARM compiler. */ + +#define INLINE_DECLARE inline + +#ifdef TXM_MODULE_MANAGER_16_MPU + +/* Define the number of MPU entries assigned to the code and data sections. + On some Cortex-M7 parts, there are 16 total entries. ThreadX uses one for access + to the kernel entry function, thus 15 remain for code and data protection. */ +#define TXM_MODULE_MPU_TOTAL_ENTRIES 16 +#define TXM_MODULE_MPU_CODE_ENTRIES 4 +#define TXM_MODULE_MPU_DATA_ENTRIES 4 +#define TXM_MODULE_MPU_SHARED_ENTRIES 3 + +#define TXM_MODULE_MPU_KERNEL_ENTRY_INDEX 0 +#define TXM_MODULE_MPU_SHARED_INDEX 9 + +#define TXM_ENABLE_REGION 0x01 + +/* There are 2 registers to set up each MPU region: MPU_RBAR, MPU_RASR. */ +typedef struct TXM_MODULE_MPU_INFO_STRUCT +{ + ULONG txm_module_mpu_region_address; + ULONG txm_module_mpu_region_attribute_size; +} TXM_MODULE_MPU_INFO; +/* Shared memory region attributes. */ +#define TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE 1 +#define TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT 0x01000000 + +/* Define the port-extensions to the module manager instance structure. */ + +#define TXM_MODULE_MANAGER_PORT_EXTENSION \ + TXM_MODULE_MPU_INFO txm_module_instance_mpu_registers[TXM_MODULE_MPU_TOTAL_ENTRIES]; \ + ULONG txm_module_instance_shared_memory_count; \ + ULONG txm_module_instance_shared_memory_address[TXM_MODULE_MPU_SHARED_ENTRIES]; \ + ULONG txm_module_instance_shared_memory_length[TXM_MODULE_MPU_SHARED_ENTRIES]; + +#else /* TXM_MODULE_MANAGER_16_MPU is not defined */ + +/* Define the number of MPU entries assigned to the code and data sections. + On Cortex-M0/M0+, M4, and some M7 parts, there are 8 total entries. ThreadX uses one for access + to the kernel entry function, thus 7 remain for code and data protection. */ +#define TXM_MODULE_MANAGER_CODE_MPU_ENTRIES 4 +#define TXM_MODULE_MANAGER_DATA_MPU_ENTRIES 3 +#define TXM_MODULE_MANAGER_SHARED_MPU_INDEX 8 +#define TXM_MODULE_MANAGER_SHARED_MPU_REGION 4 + +/* Shared memory region attributes. */ +#define TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE 1 +#define TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT 0x01000000 + +/* Define the port-extensions to the module manager instance structure. */ + +#define TXM_MODULE_MANAGER_PORT_EXTENSION \ + ULONG txm_module_instance_mpu_registers[16]; \ + ULONG txm_module_instance_shared_memory_address; \ + ULONG txm_module_instance_shared_memory_length; + +#endif /* TXM_MODULE_MANAGER_16_MPU */ + +/* Define the memory fault information structure that is populated when a memory fault occurs. */ + + +typedef struct TXM_MODULE_MANAGER_MEMORY_FAULT_INFO_STRUCT +{ + TX_THREAD *txm_module_manager_memory_fault_info_thread_ptr; + VOID *txm_module_manager_memory_fault_info_code_location; + ULONG txm_module_manager_memory_fault_info_shcsr; + ULONG txm_module_manager_memory_fault_info_cfsr; + ULONG txm_module_manager_memory_fault_info_mmfar; + ULONG txm_module_manager_memory_fault_info_bfar; + ULONG txm_module_manager_memory_fault_info_control; + ULONG txm_module_manager_memory_fault_info_sp; + ULONG txm_module_manager_memory_fault_info_r0; + ULONG txm_module_manager_memory_fault_info_r1; + ULONG txm_module_manager_memory_fault_info_r2; + ULONG txm_module_manager_memory_fault_info_r3; + ULONG txm_module_manager_memory_fault_info_r4; + ULONG txm_module_manager_memory_fault_info_r5; + ULONG txm_module_manager_memory_fault_info_r6; + ULONG txm_module_manager_memory_fault_info_r7; + ULONG txm_module_manager_memory_fault_info_r8; + ULONG txm_module_manager_memory_fault_info_r9; + ULONG txm_module_manager_memory_fault_info_r10; + ULONG txm_module_manager_memory_fault_info_r11; + ULONG txm_module_manager_memory_fault_info_r12; + ULONG txm_module_manager_memory_fault_info_lr; + ULONG txm_module_manager_memory_fault_info_xpsr; +} TXM_MODULE_MANAGER_MEMORY_FAULT_INFO; + + +#define TXM_MODULE_MANAGER_FAULT_INFO \ + TXM_MODULE_MANAGER_MEMORY_FAULT_INFO _txm_module_manager_memory_fault_info; + +/* Define the macro to check the code alignment. */ + +#define TXM_MODULE_MANAGER_CHECK_CODE_ALIGNMENT(module_location, code_alignment) \ + { \ + ULONG temp; \ + temp = (ULONG) module_location; \ + temp = temp & (code_alignment - 1); \ + if (temp) \ + { \ + _tx_mutex_put(&_txm_module_manager_mutex); \ + return(TXM_MODULE_ALIGNMENT_ERROR); \ + } \ + } + + +/* Define the macro to adjust the alignment and size for code/data areas. */ + +#define TXM_MODULE_MANAGER_ALIGNMENT_ADJUST(module_preamble, code_size, code_alignment, data_size, data_alignment) _txm_module_manager_alignment_adjust(module_preamble, &code_size, &code_alignment, &data_size, &data_alignment); + + +/* Define the macro to adjust the symbols in the module preamble. */ + +#define TXM_MODULE_MANAGER_CALCULATE_ADJUSTMENTS(properties, shell_function_adjust, start_function_adjust, stop_function_adjust, callback_function_adjust) \ + if ((properties & TXM_MODULE_COMPILER_MASK) == TXM_MODULE_IAR_COMPILER) \ + { \ + shell_function_adjust = TXM_MODULE_IAR_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_IAR_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_IAR_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_IAR_CALLBACK_ADJUST; \ + } \ + else if ((properties & TXM_MODULE_COMPILER_MASK) == TXM_MODULE_RVDS_COMPILER) \ + { \ + shell_function_adjust = TXM_MODULE_RVDS_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_RVDS_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_RVDS_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_RVDS_CALLBACK_ADJUST; \ + } \ + else \ + { \ + shell_function_adjust = TXM_MODULE_GNU_SHELL_ADJUST; \ + start_function_adjust = TXM_MODULE_GNU_START_ADJUST; \ + stop_function_adjust = TXM_MODULE_GNU_STOP_ADJUST; \ + callback_function_adjust = TXM_MODULE_GNU_CALLBACK_ADJUST; \ + } + + +/* Define the macro to populate the thread control block with module port-specific information. + Check if the module is in user mode and set up txm_module_thread_entry_info_kernel_call_dispatcher accordingly. +*/ + +#define TXM_MODULE_MANAGER_THREAD_SETUP(thread_ptr, module_instance) \ + thread_ptr -> tx_thread_module_current_user_mode = module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE; \ + thread_ptr -> tx_thread_module_user_mode = module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE; \ + if (thread_ptr -> tx_thread_module_user_mode) \ + { \ + thread_entry_info -> txm_module_thread_entry_info_kernel_call_dispatcher = _txm_module_manager_user_mode_entry; \ + } \ + else \ + { \ + thread_entry_info -> txm_module_thread_entry_info_kernel_call_dispatcher = _txm_module_manager_kernel_dispatch; \ + } + + +/* Define the macro to populate the module control block with module port-specific information. + If memory protection is enabled, set up the MPU registers. +*/ +#define TXM_MODULE_MANAGER_MODULE_SETUP(module_instance) \ + if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_USER_MODE) \ + { \ + if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) \ + { \ + _txm_module_manager_mm_register_setup(module_instance); \ + } \ + } \ + else \ + { \ + /* Do nothing. */ \ + } + +/* Define the macro to perform port-specific functions when unloading the module. */ +/* Nothing needs to be done for this port. */ +#define TXM_MODULE_MANAGER_MODULE_UNLOAD(module_instance) + + +/* Define the macros to perform port-specific checks when passing pointers to the kernel. */ + +/* Define macro to make sure object is inside the module's data. */ +#ifdef TXM_MODULE_MANAGER_16_MPU +#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \ + _txm_module_manager_inside_data_check(module_instance, obj_ptr, obj_size) +#else +#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \ + /* Check for overflow. */ \ + (((obj_ptr) < ((obj_ptr) + (obj_size))) && \ + /* Check if it's inside module data. */ \ + ((((obj_ptr) >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && \ + (((obj_ptr) + (obj_size)) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1))) || \ + /* Check if it's inside shared memory. */ \ + (((obj_ptr) >= (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address) && \ + (((obj_ptr) + (obj_size)) <= (ALIGN_TYPE) (module_instance -> txm_module_instance_shared_memory_address + module_instance -> txm_module_instance_shared_memory_length))))) +#endif + +/* Define some internal prototypes to this module port. */ + +#ifndef TX_SOURCE_CODE +#define txm_module_manager_memory_fault_notify _txm_module_manager_memory_fault_notify +#endif + + +#define TXM_MODULE_MANAGER_ADDITIONAL_PROTOTYPES \ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size, ULONG *code_alignment, ULONG *data_size, ULONG *data_alignment); \ +VOID _txm_module_manager_memory_fault_handler(VOID); \ +UINT _txm_module_manager_memory_fault_notify(VOID (*notify_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)); \ +VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance); \ +ULONG _txm_power_of_two_block_size(ULONG size); \ +ULONG _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length); \ +ULONG _txm_module_manager_region_size_get(ULONG block_size); \ +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size); + +#define TXM_MODULE_MANAGER_VERSION_ID \ +CHAR _txm_module_manager_version_id[] = \ + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M0+/IAR Version 6.1.10 *"; + +#endif diff --git a/ports_module/cortex_m0+/iar/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m0+/iar/module_lib/src/txm_module_thread_shell_entry.c new file mode 100644 index 00000000..bd1b02b1 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_lib/src/txm_module_thread_shell_entry.c @@ -0,0 +1,173 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifndef TXM_MODULE +#define TXM_MODULE +#endif + +#ifndef TX_SOURCE_CODE +#define TX_SOURCE_CODE +#endif + + +/* Include necessary system files. */ + +#include "txm_module.h" +#include "tx_thread.h" + +/* Define the global module entry pointer from the start thread of the module. */ + +TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; + + +/* Define the dispatch function pointer used in the module implementation. */ + +ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param_3); + + +/* Define the startup code that clears the uninitialized global data and sets up the + preset global variables. */ + +extern VOID __iar_data_init3(VOID); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_thread_shell_entry Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the specified entry function of the thread. It */ +/* also provides a place for the thread's entry function to return. */ +/* If the thread returns, this function places the thread in a */ +/* "COMPLETED" state. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to current thread */ +/* thread_info Pointer to thread entry info */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* __iar_data_init3 cstartup initialization */ +/* thread_entry Thread's entry function */ +/* tx_thread_resume Resume the module callback thread */ +/* _txm_module_thread_system_suspend Module thread suspension routine */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) +{ + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + VOID (*entry_exit_notify)(TX_THREAD *, UINT); +#endif + + + /* Determine if this is the start thread. If so, we must prepare the module for + execution. If not, simply skip the C startup code. */ + if (thread_info -> txm_module_thread_entry_info_start_thread) + { + /* Initialize the C environment. */ + __iar_data_init3(); + + /* Save the entry info pointer, for later use. */ + _txm_module_entry_info = thread_info; + + /* Save the kernel function dispatch address. This is used to make all resident calls from + the module. */ + _txm_module_kernel_call_dispatcher = thread_info -> txm_module_thread_entry_info_kernel_call_dispatcher; + + /* Ensure that we have a valid pointer. */ + while (!_txm_module_kernel_call_dispatcher) + { + /* Loop here, if an error is present getting the dispatch function pointer! + An error here typically indicates the resident portion of _tx_thread_schedule + is not supporting the trap to obtain the function pointer. */ + } + + /* Resume the module's callback thread, already created in the manager. */ + _txe_thread_resume(thread_info -> txm_module_thread_entry_info_callback_request_thread); + } + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + + /* Pickup the entry/exit application callback routine. */ + entry_exit_notify = thread_info -> txm_module_thread_entry_info_exit_notify; + + /* Determine if an application callback routine is specified. */ + if (entry_exit_notify != TX_NULL) + { + + /* Yes, notify application that this thread has been entered! */ + (entry_exit_notify)(thread_ptr, TX_THREAD_ENTRY); + } +#endif + + /* Call current thread's entry function. */ + (thread_info -> txm_module_thread_entry_info_entry) (thread_info -> txm_module_thread_entry_info_parameter); + + /* Suspend thread with a "completed" state. */ + + +#ifndef TX_DISABLE_NOTIFY_CALLBACKS + + /* Pickup the entry/exit application callback routine again. */ + entry_exit_notify = thread_info -> txm_module_thread_entry_info_exit_notify; + + /* Determine if an application callback routine is specified. */ + if (entry_exit_notify != TX_NULL) + { + + /* Yes, notify application that this thread has exited! */ + (entry_exit_notify)(thread_ptr, TX_THREAD_EXIT); + } +#endif + + /* Call actual thread suspension routine. */ + _txm_module_thread_system_suspend(thread_ptr); + +#ifdef TX_SAFETY_CRITICAL + + /* If we ever get here, raise safety critical exception. */ + TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); +#endif +} diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_iar.c b/ports_module/cortex_m0+/iar/module_manager/src/tx_iar.c new file mode 100644 index 00000000..dd719370 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_iar.c @@ -0,0 +1,804 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** IAR Multithreaded Library Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Define IAR library for tools prior to version 8. */ + +#if (__VER__ < 8000000) + + +/* IAR version 7 and below. */ + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_initialize.h" +#include "tx_thread.h" +#include "tx_mutex.h" + + +/* This implementation requires that the following macros are defined in the + tx_port.h file and is included with the following code segments: + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_iar_tls_pointer; +#else +#define TX_THREAD_EXTENSION_2 +#endif + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = __iar_dlib_perthread_allocate(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) __iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION __iar_dlib_perthread_access(0); +#else +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#endif + + This should be done automatically if TX_ENABLE_IAR_LIBRARY_SUPPORT is defined while building the ThreadX library and the + application. + + Finally, the project options General Options -> Library Configuration should have the "Enable thread support in library" box selected. +*/ + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT + +#include + + +#if _MULTI_THREAD + +TX_MUTEX __tx_iar_system_lock_mutexes[_MAX_LOCK]; +UINT __tx_iar_system_lock_next_free_mutex = 0; + + +/* Define error counters, just for debug purposes. */ + +UINT __tx_iar_system_lock_no_mutexes; +UINT __tx_iar_system_lock_internal_errors; +UINT __tx_iar_system_lock_isr_caller; + + +/* Define the TLS access function for the IAR library. */ + +void _DLIB_TLS_MEMORY *__iar_dlib_perthread_access(void _DLIB_TLS_MEMORY *symbp) +{ + +char _DLIB_TLS_MEMORY *p = 0; + + /* Is there a current thread? */ + if (_tx_thread_current_ptr) + p = (char _DLIB_TLS_MEMORY *) _tx_thread_current_ptr -> tx_thread_iar_tls_pointer; + else + p = (void _DLIB_TLS_MEMORY *) __segment_begin("__DLIB_PERTHREAD"); + p += __IAR_DLIB_PERTHREAD_SYMBOL_OFFSET(symbp); + return (void _DLIB_TLS_MEMORY *) p; +} + + +/* Define mutexes for IAR library. */ + +void __iar_system_Mtxinit(__iar_Rmtx *m) +{ + +UINT i; +UINT status; +TX_MUTEX *mutex_ptr; + + + /* First, find a free mutex in the list. */ + for (i = 0; i < _MAX_LOCK; i++) + { + + /* Setup a pointer to the start of the next free mutex. */ + mutex_ptr = &__tx_iar_system_lock_mutexes[__tx_iar_system_lock_next_free_mutex++]; + + /* Check for wrap-around on the next free mutex. */ + if (__tx_iar_system_lock_next_free_mutex >= _MAX_LOCK) + { + + /* Yes, set the free index back to 0. */ + __tx_iar_system_lock_next_free_mutex = 0; + } + + /* Is this mutex free? */ + if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID) + { + + /* Yes, this mutex is free, get out of the loop! */ + break; + } + } + + /* Determine if a free mutex was found. */ + if (i >= _MAX_LOCK) + { + + /* Error! No more free mutexes! */ + + /* Increment the no mutexes error counter. */ + __tx_iar_system_lock_no_mutexes++; + + /* Set return pointer to NULL. */ + *m = TX_NULL; + + /* Return. */ + return; + } + + /* Now create the ThreadX mutex for the IAR library. */ + status = _tx_mutex_create(mutex_ptr, "IAR System Library Lock", TX_NO_INHERIT); + + /* Determine if the creation was successful. */ + if (status == TX_SUCCESS) + { + + /* Yes, successful creation, return mutex pointer. */ + *m = (VOID *) mutex_ptr; + } + else + { + + /* Increment the internal error counter. */ + __tx_iar_system_lock_internal_errors++; + + /* Return a NULL pointer to indicate an error. */ + *m = TX_NULL; + } +} + +void __iar_system_Mtxdst(__iar_Rmtx *m) +{ + + /* Simply delete the mutex. */ + _tx_mutex_delete((TX_MUTEX *) *m); +} + +void __iar_system_Mtxlock(__iar_Rmtx *m) +{ + +UINT status; + + + /* Determine the caller's context. Mutex locks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Get the mutex. */ + status = _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_system_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_system_lock_isr_caller++; + } +} + +void __iar_system_Mtxunlock(__iar_Rmtx *m) +{ + +UINT status; + + + /* Determine the caller's context. Mutex unlocks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Release the mutex. */ + status = _tx_mutex_put((TX_MUTEX *) *m); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_system_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_system_lock_isr_caller++; + } +} + + +#if _DLIB_FILE_DESCRIPTOR + +TX_MUTEX __tx_iar_file_lock_mutexes[_MAX_FLOCK]; +UINT __tx_iar_file_lock_next_free_mutex = 0; + + +/* Define error counters, just for debug purposes. */ + +UINT __tx_iar_file_lock_no_mutexes; +UINT __tx_iar_file_lock_internal_errors; +UINT __tx_iar_file_lock_isr_caller; + + +void __iar_file_Mtxinit(__iar_Rmtx *m) +{ + +UINT i; +UINT status; +TX_MUTEX *mutex_ptr; + + + /* First, find a free mutex in the list. */ + for (i = 0; i < _MAX_FLOCK; i++) + { + + /* Setup a pointer to the start of the next free mutex. */ + mutex_ptr = &__tx_iar_file_lock_mutexes[__tx_iar_file_lock_next_free_mutex++]; + + /* Check for wrap-around on the next free mutex. */ + if (__tx_iar_file_lock_next_free_mutex >= _MAX_LOCK) + { + + /* Yes, set the free index back to 0. */ + __tx_iar_file_lock_next_free_mutex = 0; + } + + /* Is this mutex free? */ + if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID) + { + + /* Yes, this mutex is free, get out of the loop! */ + break; + } + } + + /* Determine if a free mutex was found. */ + if (i >= _MAX_LOCK) + { + + /* Error! No more free mutexes! */ + + /* Increment the no mutexes error counter. */ + __tx_iar_file_lock_no_mutexes++; + + /* Set return pointer to NULL. */ + *m = TX_NULL; + + /* Return. */ + return; + } + + /* Now create the ThreadX mutex for the IAR library. */ + status = _tx_mutex_create(mutex_ptr, "IAR File Library Lock", TX_NO_INHERIT); + + /* Determine if the creation was successful. */ + if (status == TX_SUCCESS) + { + + /* Yes, successful creation, return mutex pointer. */ + *m = (VOID *) mutex_ptr; + } + else + { + + /* Increment the internal error counter. */ + __tx_iar_file_lock_internal_errors++; + + /* Return a NULL pointer to indicate an error. */ + *m = TX_NULL; + } +} + +void __iar_file_Mtxdst(__iar_Rmtx *m) +{ + + /* Simply delete the mutex. */ + _tx_mutex_delete((TX_MUTEX *) *m); +} + +void __iar_file_Mtxlock(__iar_Rmtx *m) +{ + +UINT status; + + + /* Determine the caller's context. Mutex locks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Get the mutex. */ + status = _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_file_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_file_lock_isr_caller++; + } +} + +void __iar_file_Mtxunlock(__iar_Rmtx *m) +{ + +UINT status; + + + /* Determine the caller's context. Mutex unlocks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Release the mutex. */ + status = _tx_mutex_put((TX_MUTEX *) *m); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_file_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_file_lock_isr_caller++; + } +} +#endif /* _DLIB_FILE_DESCRIPTOR */ + +#endif /* _MULTI_THREAD */ + +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + +#else /* IAR version 8 and above. */ + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_initialize.h" +#include "tx_thread.h" +#include "tx_mutex.h" + +/* This implementation requires that the following macros are defined in the + tx_port.h file and is included with the following code segments: + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_iar_tls_pointer; +#else +#define TX_THREAD_EXTENSION_2 +#endif + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = __iar_dlib_perthread_allocate(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {__iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); +#else +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#endif + + This should be done automatically if TX_ENABLE_IAR_LIBRARY_SUPPORT is defined while building the ThreadX library and the + application. + + Finally, the project options General Options -> Library Configuration should have the "Enable thread support in library" box selected. +*/ + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT + +#include + + +void * __aeabi_read_tp(); + +void* _tx_iar_create_per_thread_tls_area(); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); + +#pragma section="__iar_tls$$DATA" + +/* Define the TLS access function for the IAR library. */ +void * __aeabi_read_tp(void) +{ + void *p = 0; + TX_THREAD *thread_ptr = _tx_thread_current_ptr; + if (thread_ptr) + { + p = thread_ptr->tx_thread_iar_tls_pointer; + } + else + { + p = __section_begin("__iar_tls$$DATA"); + } + return p; +} + +/* Define the TLS creation and destruction to use malloc/free. */ + +void* _tx_iar_create_per_thread_tls_area() +{ + UINT tls_size = __iar_tls_size(); + + /* Get memory for TLS. */ + void *p = malloc(tls_size); + + /* Initialize TLS-area and run constructors for objects in TLS */ + __iar_tls_init(p); + return p; +} + +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr) +{ + /* Destroy objects living in TLS */ + __call_thread_dtors(); + free(tls_ptr); +} + +#ifndef _MAX_LOCK +#define _MAX_LOCK 4 +#endif + +static TX_MUTEX __tx_iar_system_lock_mutexes[_MAX_LOCK]; +static UINT __tx_iar_system_lock_next_free_mutex = 0; + + +/* Define error counters, just for debug purposes. */ + +UINT __tx_iar_system_lock_no_mutexes; +UINT __tx_iar_system_lock_internal_errors; +UINT __tx_iar_system_lock_isr_caller; + + +/* Define mutexes for IAR library. */ + +void __iar_system_Mtxinit(__iar_Rmtx *m) +{ + +UINT i; +UINT status; +TX_MUTEX *mutex_ptr; + + + /* First, find a free mutex in the list. */ + for (i = 0; i < _MAX_LOCK; i++) + { + + /* Setup a pointer to the start of the next free mutex. */ + mutex_ptr = &__tx_iar_system_lock_mutexes[__tx_iar_system_lock_next_free_mutex++]; + + /* Check for wrap-around on the next free mutex. */ + if (__tx_iar_system_lock_next_free_mutex >= _MAX_LOCK) + { + + /* Yes, set the free index back to 0. */ + __tx_iar_system_lock_next_free_mutex = 0; + } + + /* Is this mutex free? */ + if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID) + { + + /* Yes, this mutex is free, get out of the loop! */ + break; + } + } + + /* Determine if a free mutex was found. */ + if (i >= _MAX_LOCK) + { + + /* Error! No more free mutexes! */ + + /* Increment the no mutexes error counter. */ + __tx_iar_system_lock_no_mutexes++; + + /* Set return pointer to NULL. */ + *m = TX_NULL; + + /* Return. */ + return; + } + + /* Now create the ThreadX mutex for the IAR library. */ + status = _tx_mutex_create(mutex_ptr, "IAR System Library Lock", TX_NO_INHERIT); + + /* Determine if the creation was successful. */ + if (status == TX_SUCCESS) + { + + /* Yes, successful creation, return mutex pointer. */ + *m = (VOID *) mutex_ptr; + } + else + { + + /* Increment the internal error counter. */ + __tx_iar_system_lock_internal_errors++; + + /* Return a NULL pointer to indicate an error. */ + *m = TX_NULL; + } +} + +void __iar_system_Mtxdst(__iar_Rmtx *m) +{ + + /* Simply delete the mutex. */ + _tx_mutex_delete((TX_MUTEX *) *m); +} + +void __iar_system_Mtxlock(__iar_Rmtx *m) +{ + if (*m) + { + UINT status; + + /* Determine the caller's context. Mutex locks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Get the mutex. */ + status = _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_system_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_system_lock_isr_caller++; + } + } +} + +void __iar_system_Mtxunlock(__iar_Rmtx *m) +{ + if (*m) + { + UINT status; + + /* Determine the caller's context. Mutex unlocks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Release the mutex. */ + status = _tx_mutex_put((TX_MUTEX *) *m); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_system_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_system_lock_isr_caller++; + } + } +} + + +#if _DLIB_FILE_DESCRIPTOR + +#include /* Added to get access to FOPEN_MAX */ +#ifndef _MAX_FLOCK +#define _MAX_FLOCK FOPEN_MAX /* Define _MAX_FLOCK as the maximum number of open files */ +#endif + + +TX_MUTEX __tx_iar_file_lock_mutexes[_MAX_FLOCK]; +UINT __tx_iar_file_lock_next_free_mutex = 0; + + +/* Define error counters, just for debug purposes. */ + +UINT __tx_iar_file_lock_no_mutexes; +UINT __tx_iar_file_lock_internal_errors; +UINT __tx_iar_file_lock_isr_caller; + + +void __iar_file_Mtxinit(__iar_Rmtx *m) +{ + +UINT i; +UINT status; +TX_MUTEX *mutex_ptr; + + + /* First, find a free mutex in the list. */ + for (i = 0; i < _MAX_FLOCK; i++) + { + + /* Setup a pointer to the start of the next free mutex. */ + mutex_ptr = &__tx_iar_file_lock_mutexes[__tx_iar_file_lock_next_free_mutex++]; + + /* Check for wrap-around on the next free mutex. */ + if (__tx_iar_file_lock_next_free_mutex >= _MAX_LOCK) + { + + /* Yes, set the free index back to 0. */ + __tx_iar_file_lock_next_free_mutex = 0; + } + + /* Is this mutex free? */ + if (mutex_ptr -> tx_mutex_id != TX_MUTEX_ID) + { + + /* Yes, this mutex is free, get out of the loop! */ + break; + } + } + + /* Determine if a free mutex was found. */ + if (i >= _MAX_LOCK) + { + + /* Error! No more free mutexes! */ + + /* Increment the no mutexes error counter. */ + __tx_iar_file_lock_no_mutexes++; + + /* Set return pointer to NULL. */ + *m = TX_NULL; + + /* Return. */ + return; + } + + /* Now create the ThreadX mutex for the IAR library. */ + status = _tx_mutex_create(mutex_ptr, "IAR File Library Lock", TX_NO_INHERIT); + + /* Determine if the creation was successful. */ + if (status == TX_SUCCESS) + { + + /* Yes, successful creation, return mutex pointer. */ + *m = (VOID *) mutex_ptr; + } + else + { + + /* Increment the internal error counter. */ + __tx_iar_file_lock_internal_errors++; + + /* Return a NULL pointer to indicate an error. */ + *m = TX_NULL; + } +} + +void __iar_file_Mtxdst(__iar_Rmtx *m) +{ + + /* Simply delete the mutex. */ + _tx_mutex_delete((TX_MUTEX *) *m); +} + +void __iar_file_Mtxlock(__iar_Rmtx *m) +{ + +UINT status; + + + /* Determine the caller's context. Mutex locks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Get the mutex. */ + status = _tx_mutex_get((TX_MUTEX *) *m, TX_WAIT_FOREVER); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_file_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_file_lock_isr_caller++; + } +} + +void __iar_file_Mtxunlock(__iar_Rmtx *m) +{ + +UINT status; + + + /* Determine the caller's context. Mutex unlocks are only available from initialization and + threads. */ + if ((_tx_thread_system_state == 0) || (_tx_thread_system_state >= TX_INITIALIZE_IN_PROGRESS)) + { + + /* Release the mutex. */ + status = _tx_mutex_put((TX_MUTEX *) *m); + + /* Check the status of the mutex release. */ + if (status) + { + + /* Internal error, increment the counter. */ + __tx_iar_file_lock_internal_errors++; + } + } + else + { + + /* Increment the ISR caller error. */ + __tx_iar_file_lock_isr_caller++; + } +} +#endif /* _DLIB_FILE_DESCRIPTOR */ + +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + +#endif /* IAR version 8 and above. */ diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_misra.s b/ports_module/cortex_m0+/iar/module_manager/src/tx_misra.s new file mode 100644 index 00000000..53c60fbc --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_misra.s @@ -0,0 +1,1074 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX MISRA Compliance */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + #define SHT_PROGBITS 0x1 + + EXTERN __aeabi_memset + EXTERN _tx_thread_current_ptr + EXTERN _tx_thread_interrupt_disable + EXTERN _tx_thread_interrupt_restore + EXTERN _tx_thread_stack_analyze + EXTERN _tx_thread_stack_error_handler + EXTERN _tx_thread_system_state +#ifdef TX_ENABLE_EVENT_TRACE + EXTERN _tx_trace_buffer_current_ptr + EXTERN _tx_trace_buffer_end_ptr + EXTERN _tx_trace_buffer_start_ptr + EXTERN _tx_trace_event_enable_bits + EXTERN _tx_trace_full_notify_function + EXTERN _tx_trace_header_ptr +#endif + + PUBLIC _tx_misra_always_true + PUBLIC _tx_misra_block_pool_to_uchar_pointer_convert + PUBLIC _tx_misra_byte_pool_to_uchar_pointer_convert + PUBLIC _tx_misra_char_to_uchar_pointer_convert + PUBLIC _tx_misra_const_char_to_char_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + PUBLIC _tx_misra_entry_to_uchar_pointer_convert +#endif + PUBLIC _tx_misra_indirect_void_to_uchar_pointer_convert + PUBLIC _tx_misra_memset + PUBLIC _tx_misra_message_copy +#ifdef TX_ENABLE_EVENT_TRACE + PUBLIC _tx_misra_object_to_uchar_pointer_convert +#endif + PUBLIC _tx_misra_pointer_to_ulong_convert + PUBLIC _tx_misra_status_get + PUBLIC _tx_misra_thread_stack_check +#ifdef TX_ENABLE_EVENT_TRACE + PUBLIC _tx_misra_time_stamp_get +#endif + PUBLIC _tx_misra_timer_indirect_to_void_pointer_convert + PUBLIC _tx_misra_timer_pointer_add + PUBLIC _tx_misra_timer_pointer_dif +#ifdef TX_ENABLE_EVENT_TRACE + PUBLIC _tx_misra_trace_event_insert +#endif + PUBLIC _tx_misra_uchar_pointer_add + PUBLIC _tx_misra_uchar_pointer_dif + PUBLIC _tx_misra_uchar_pointer_sub + PUBLIC _tx_misra_uchar_to_align_type_pointer_convert + PUBLIC _tx_misra_uchar_to_block_pool_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + PUBLIC _tx_misra_uchar_to_entry_pointer_convert + PUBLIC _tx_misra_uchar_to_header_pointer_convert +#endif + PUBLIC _tx_misra_uchar_to_indirect_byte_pool_pointer_convert + PUBLIC _tx_misra_uchar_to_indirect_uchar_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + PUBLIC _tx_misra_uchar_to_object_pointer_convert +#endif + PUBLIC _tx_misra_uchar_to_void_pointer_convert + PUBLIC _tx_misra_ulong_pointer_add + PUBLIC _tx_misra_ulong_pointer_dif + PUBLIC _tx_misra_ulong_pointer_sub + PUBLIC _tx_misra_ulong_to_pointer_convert + PUBLIC _tx_misra_ulong_to_thread_pointer_convert + PUBLIC _tx_misra_user_timer_pointer_get + PUBLIC _tx_misra_void_to_block_pool_pointer_convert + PUBLIC _tx_misra_void_to_byte_pool_pointer_convert + PUBLIC _tx_misra_void_to_event_flags_pointer_convert + PUBLIC _tx_misra_void_to_indirect_uchar_pointer_convert + PUBLIC _tx_misra_void_to_mutex_pointer_convert + PUBLIC _tx_misra_void_to_queue_pointer_convert + PUBLIC _tx_misra_void_to_semaphore_pointer_convert + PUBLIC _tx_misra_void_to_thread_pointer_convert + PUBLIC _tx_misra_void_to_uchar_pointer_convert + PUBLIC _tx_misra_void_to_ulong_pointer_convert + PUBLIC _tx_misra_ipsr_get + PUBLIC _tx_misra_control_get + PUBLIC _tx_misra_control_set +#ifdef __ARMVFP__ + PUBLIC _tx_misra_fpccr_get + PUBLIC _tx_misra_vfp_touch +#endif + PUBLIC _tx_version_id + + + SECTION `.data`:DATA:REORDER:NOROOT(2) + DATA +// 51 CHAR _tx_version_id[100] = "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX 6.1 MISRA C Compliant *"; +_tx_version_id: + DC8 43H, 6FH, 70H, 79H, 72H, 69H, 67H, 68H + DC8 74H, 20H, 28H, 63H, 29H, 20H, 31H, 39H + DC8 39H, 36H, 2DH, 32H, 30H, 31H, 38H, 20H + DC8 45H, 78H, 70H, 72H, 65H, 73H, 73H, 20H + DC8 4CH, 6FH, 67H, 69H, 63H, 20H, 49H, 6EH + DC8 63H, 2EH, 20H, 2AH, 20H, 54H, 68H, 72H + DC8 65H, 61H, 64H, 58H, 20H, 35H, 2EH, 38H + DC8 20H, 4DH, 49H, 53H, 52H, 41H, 20H, 43H + DC8 20H, 43H, 6FH, 6DH, 70H, 6CH, 69H, 61H + DC8 6EH, 74H, 20H, 2AH, 0 + DC8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_memset(VOID *ptr, UINT value, UINT size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_memset: + PUSH {R4,LR} + MOVS R4,R0 + MOVS R0,R2 + MOVS R2,R1 + MOVS R1,R0 + MOVS R0,R4 + BL __aeabi_memset + POP {R4,PC} ;; return + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UCHAR *_tx_misra_uchar_pointer_add(UCHAR *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_pointer_add: + ADD R0,R0,R1 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UCHAR *_tx_misra_uchar_pointer_sub(UCHAR *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_pointer_sub: + RSBS R1,R1,#+0 + ADD R0,R0,R1 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_uchar_pointer_dif(UCHAR *ptr1, UCHAR *ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_pointer_dif: + SUBS R0,R0,R1 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_pointer_to_ulong_convert(VOID *ptr); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_pointer_to_ulong_convert: + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG *_tx_misra_ulong_pointer_add(ULONG *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_ulong_pointer_add: + ADD R0,R0,R1, LSL #+2 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG *_tx_misra_ulong_pointer_sub(ULONG *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_ulong_pointer_sub: + MVNS R2,#+3 + MULS R1,R2,R1 + ADD R0,R0,R1 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_ulong_pointer_dif(ULONG *ptr1, ULONG *ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_ulong_pointer_dif: + SUBS R0,R0,R1 + ASRS R0,R0,#+2 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID *_tx_misra_ulong_to_pointer_convert(ULONG input); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_ulong_to_pointer_convert: + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_message_copy(ULONG **source, ULONG **destination, */ +/** UINT size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_message_copy: + PUSH {R4,R5} + LDR R3,[R0, #+0] + LDR R4,[R1, #+0] + LDR R5,[R3, #+0] + STR R5,[R4, #+0] + ADDS R4,R4,#+4 + ADDS R3,R3,#+4 + CMP R2,#+2 + BCC.N ??_tx_misra_message_copy_0 + SUBS R2,R2,#+1 + B.N ??_tx_misra_message_copy_1 +??_tx_misra_message_copy_2: + LDR R5,[R3, #+0] + STR R5,[R4, #+0] + ADDS R4,R4,#+4 + ADDS R3,R3,#+4 + SUBS R2,R2,#+1 +??_tx_misra_message_copy_1: + CMP R2,#+0 + BNE.N ??_tx_misra_message_copy_2 +??_tx_misra_message_copy_0: + STR R3,[R0, #+0] + STR R4,[R1, #+0] + POP {R4,R5} + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_timer_pointer_dif(TX_TIMER_INTERNAL **ptr1, */ +/** TX_TIMER_INTERNAL **ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_timer_pointer_dif: + SUBS R0,R0,R1 + ASRS R0,R0,#+2 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** TX_TIMER_INTERNAL **_tx_misra_timer_pointer_add(TX_TIMER_INTERNAL */ +/** **ptr1, ULONG size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_timer_pointer_add: + ADD R0,R0,R1, LSL #+2 + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_user_timer_pointer_get(TX_TIMER_INTERNAL */ +/** *internal_timer, TX_TIMER **user_timer); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_user_timer_pointer_get: + ADDS R2,R0,#+8 + SUBS R2,R2,R0 + RSBS R2,R2,#+0 + ADD R0,R0,R2 + STR R0,[R1, #+0] + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_thread_stack_check(TX_THREAD *thread_ptr, */ +/** VOID **highest_stack); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_thread_stack_check: + PUSH {R3-R5,LR} + MOVS R4,R0 + MOVS R5,R1 + BL _tx_thread_interrupt_disable + CMP R4,#+0 + BEQ.N ??_tx_misra_thread_stack_check_0 + LDR R1,[R4, #+0] + LDR.N R2,??DataTable2 ;; 0x54485244 + CMP R1,R2 + BNE.N ??_tx_misra_thread_stack_check_0 + LDR R1,[R4, #+8] + LDR R2,[R5, #+0] + CMP R1,R2 + BCS.N ??_tx_misra_thread_stack_check_1 + LDR R1,[R4, #+8] + STR R1,[R5, #+0] +??_tx_misra_thread_stack_check_1: + LDR R1,[R4, #+12] + LDR R1,[R1, #+0] + CMP R1,#-269488145 + BNE.N ??_tx_misra_thread_stack_check_2 + LDR R1,[R4, #+16] + LDR R1,[R1, #+1] + CMP R1,#-269488145 + BNE.N ??_tx_misra_thread_stack_check_2 + LDR R1,[R5, #+0] + LDR R2,[R4, #+12] + CMP R1,R2 + BCS.N ??_tx_misra_thread_stack_check_3 +??_tx_misra_thread_stack_check_2: + BL _tx_thread_interrupt_restore + MOVS R0,R4 + BL _tx_thread_stack_error_handler + BL _tx_thread_interrupt_disable +??_tx_misra_thread_stack_check_3: + LDR R1,[R5, #+0] + LDR R1,[R1, #-4] + CMP R1,#-269488145 + BEQ.N ??_tx_misra_thread_stack_check_0 + BL _tx_thread_interrupt_restore + MOVS R0,R4 + BL _tx_thread_stack_analyze + BL _tx_thread_interrupt_disable +??_tx_misra_thread_stack_check_0: + BL _tx_thread_interrupt_restore + POP {R0,R4,R5,PC} ;; return + +#ifdef TX_ENABLE_EVENT_TRACE + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_trace_event_insert(ULONG event_id, */ +/** VOID *info_field_1, ULONG info_field_2, ULONG info_field_3, */ +/** ULONG info_field_4, ULONG filter, ULONG time_stamp); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_trace_event_insert: + PUSH {R3-R7,LR} + LDR.N R4,??DataTable2_1 + LDR R4,[R4, #+0] + CMP R4,#+0 + BEQ.N ??_tx_misra_trace_event_insert_0 + LDR.N R5,??DataTable2_2 + LDR R5,[R5, #+0] + LDR R6,[SP, #+28] + TST R5,R6 + BEQ.N ??_tx_misra_trace_event_insert_0 + LDR.N R5,??DataTable2_3 + LDR R5,[R5, #+0] + LDR.N R6,??DataTable2_4 + LDR R6,[R6, #+0] + CMP R5,#+0 + BNE.N ??_tx_misra_trace_event_insert_1 + LDR R5,[R6, #+44] + LDR R7,[R6, #+60] + LSLS R7,R7,#+16 + ORRS R7,R7,#0x80000000 + ORRS R5,R7,R5 + B.N ??_tx_misra_trace_event_insert_2 +??_tx_misra_trace_event_insert_1: + CMP R5,#-252645136 + BCS.N ??_tx_misra_trace_event_insert_3 + MOVS R5,R6 + MOVS R6,#-1 + B.N ??_tx_misra_trace_event_insert_2 +??_tx_misra_trace_event_insert_3: + MOVS R6,#-252645136 + MOVS R5,#+0 +??_tx_misra_trace_event_insert_2: + STR R6,[R4, #+0] + STR R5,[R4, #+4] + STR R0,[R4, #+8] + LDR R0,[SP, #+32] + STR R0,[R4, #+12] + STR R1,[R4, #+16] + STR R2,[R4, #+20] + STR R3,[R4, #+24] + LDR R0,[SP, #+24] + STR R0,[R4, #+28] + ADDS R4,R4,#+32 + LDR.N R0,??DataTable2_5 + LDR R0,[R0, #+0] + CMP R4,R0 + BCC.N ??_tx_misra_trace_event_insert_4 + LDR.N R0,??DataTable2_6 + LDR R4,[R0, #+0] + LDR.N R0,??DataTable2_1 + STR R4,[R0, #+0] + LDR.N R0,??DataTable2_7 + LDR R0,[R0, #+0] + STR R4,[R0, #+32] + LDR.N R0,??DataTable2_8 + LDR R0,[R0, #+0] + CMP R0,#+0 + BEQ.N ??_tx_misra_trace_event_insert_0 + LDR.N R0,??DataTable2_7 + LDR R0,[R0, #+0] + LDR.N R1,??DataTable2_8 + LDR R1,[R1, #+0] + BLX R1 + B.N ??_tx_misra_trace_event_insert_0 +??_tx_misra_trace_event_insert_4: + LDR.N R0,??DataTable2_1 + STR R4,[R0, #+0] + LDR.N R0,??DataTable2_7 + LDR R0,[R0, #+0] + STR R4,[R0, #+32] +??_tx_misra_trace_event_insert_0: + POP {R0,R4-R7,PC} ;; return + + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_1: + DC32 _tx_trace_buffer_current_ptr + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_2: + DC32 _tx_trace_event_enable_bits + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_5: + DC32 _tx_trace_buffer_end_ptr + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_6: + DC32 _tx_trace_buffer_start_ptr + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_7: + DC32 _tx_trace_header_ptr + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_8: + DC32 _tx_trace_full_notify_function + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_time_stamp_get(VOID); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_time_stamp_get: + MOVS R0,#+0 + BX LR ;; return + +#endif + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2: + DC32 0x54485244 + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_3: + DC32 _tx_thread_system_state + + SECTION `.text`:CODE:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA +??DataTable2_4: + DC32 _tx_thread_current_ptr + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UINT _tx_misra_always_true(void); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_always_true: + MOVS R0,#+1 + BX LR ;; return + + +/******************************************************************************************/ +/******************************************************************************************/ +/** */ +/** UCHAR **_tx_misra_indirect_void_to_uchar_pointer_convert(VOID **return_ptr); */ +/** */ +/******************************************************************************************/ +/******************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_indirect_void_to_uchar_pointer_convert: + BX LR ;; return + + +/***************************************************************************************/ +/***************************************************************************************/ +/** */ +/** UCHAR **_tx_misra_uchar_to_indirect_uchar_pointer_convert(UCHAR *pointer); */ +/** */ +/***************************************************************************************/ +/***************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_indirect_uchar_pointer_convert: + BX LR ;; return + + +/***********************************************************************************/ +/***********************************************************************************/ +/** */ +/** UCHAR *_tx_misra_block_pool_to_uchar_pointer_convert(TX_BLOCK_POOL *pool); */ +/** */ +/***********************************************************************************/ +/***********************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_block_pool_to_uchar_pointer_convert: + BX LR ;; return + + +/******************************************************************************************/ +/******************************************************************************************/ +/** */ +/** TX_BLOCK_POOL *_tx_misra_void_to_block_pool_pointer_convert(VOID *pointer); */ +/** */ +/******************************************************************************************/ +/******************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_block_pool_pointer_convert: + BX LR ;; return + + +/*****************************************************************************/ +/*****************************************************************************/ +/** */ +/** UCHAR *_tx_misra_void_to_uchar_pointer_convert(VOID *pointer); */ +/** */ +/*****************************************************************************/ +/*****************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_uchar_pointer_convert: + BX LR ;; return + + +/************************************************************************************/ +/************************************************************************************/ +/** */ +/** TX_BLOCK_POOL *_tx_misra_uchar_to_block_pool_pointer_convert(UCHAR *pointer); */ +/** */ +/************************************************************************************/ +/************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_block_pool_pointer_convert: + BX LR ;; return + + +/**************************************************************************************/ +/**************************************************************************************/ +/** */ +/** UCHAR **_tx_misra_void_to_indirect_uchar_pointer_convert(VOID *pointer); */ +/** */ +/**************************************************************************************/ +/**************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_indirect_uchar_pointer_convert: + BX LR ;; return + + +/*****************************************************************************************/ +/*****************************************************************************************/ +/** */ +/** TX_BYTE_POOL *_tx_misra_void_to_byte_pool_pointer_convert(VOID *pointer); */ +/** */ +/*****************************************************************************************/ +/*****************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_byte_pool_pointer_convert: + BX LR ;; return + + +/***************************************************************************************/ +/***************************************************************************************/ +/** */ +/** UCHAR *_tx_misra_byte_pool_to_uchar_pointer_convert(TX_BYTE_POOL *pool); */ +/** */ +/***************************************************************************************/ +/***************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_byte_pool_to_uchar_pointer_convert: + BX LR ;; return + + +/*****************************************************************************************/ +/*****************************************************************************************/ +/** */ +/** ALIGN_TYPE *_tx_misra_uchar_to_align_type_pointer_convert(UCHAR *pointer); */ +/** */ +/*****************************************************************************************/ +/*****************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_align_type_pointer_convert: + BX LR ;; return + + +/****************************************************************************************************/ +/****************************************************************************************************/ +/** */ +/** TX_BYTE_POOL **_tx_misra_uchar_to_indirect_byte_pool_pointer_convert(UCHAR *pointer); */ +/** */ +/****************************************************************************************************/ +/****************************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_indirect_byte_pool_pointer_convert: + BX LR ;; return + + +/**************************************************************************************************/ +/**************************************************************************************************/ +/** */ +/** TX_EVENT_FLAGS_GROUP *_tx_misra_void_to_event_flags_pointer_convert(VOID *pointer); */ +/** */ +/**************************************************************************************************/ +/**************************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_event_flags_pointer_convert: + BX LR ;; return + + +/*****************************************************************************/ +/*****************************************************************************/ +/** */ +/** ULONG *_tx_misra_void_to_ulong_pointer_convert(VOID *pointer); */ +/** */ +/*****************************************************************************/ +/*****************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_ulong_pointer_convert: + BX LR ;; return + + +/********************************************************************************/ +/********************************************************************************/ +/** */ +/** TX_MUTEX *_tx_misra_void_to_mutex_pointer_convert(VOID *pointer); */ +/** */ +/********************************************************************************/ +/********************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_mutex_pointer_convert: + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UINT _tx_misra_status_get(UINT status); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_status_get: + MOVS R0,#+0 + BX LR ;; return + + +/********************************************************************************/ +/********************************************************************************/ +/** */ +/** TX_QUEUE *_tx_misra_void_to_queue_pointer_convert(VOID *pointer); */ +/** */ +/********************************************************************************/ +/********************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_queue_pointer_convert: + BX LR ;; return + + +/****************************************************************************************/ +/****************************************************************************************/ +/** */ +/** TX_SEMAPHORE *_tx_misra_void_to_semaphore_pointer_convert(VOID *pointer); */ +/** */ +/****************************************************************************************/ +/****************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_semaphore_pointer_convert: + BX LR ;; return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID *_tx_misra_uchar_to_void_pointer_convert(UCHAR *pointer); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_void_pointer_convert: + BX LR ;; return + + +/*********************************************************************************/ +/*********************************************************************************/ +/** */ +/** TX_THREAD *_tx_misra_ulong_to_thread_pointer_convert(ULONG value); */ +/** */ +/*********************************************************************************/ +/*********************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_ulong_to_thread_pointer_convert: + BX LR ;; return + + +/***************************************************************************************************/ +/***************************************************************************************************/ +/** */ +/** VOID *_tx_misra_timer_indirect_to_void_pointer_convert(TX_TIMER_INTERNAL **pointer); */ +/** */ +/***************************************************************************************************/ +/***************************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_timer_indirect_to_void_pointer_convert: + BX LR ;; return + + +/***************************************************************************************/ +/***************************************************************************************/ +/** */ +/** CHAR *_tx_misra_const_char_to_char_pointer_convert(const char *pointer); */ +/** */ +/***************************************************************************************/ +/***************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_const_char_to_char_pointer_convert: + BX LR ;; return + + +/**********************************************************************************/ +/**********************************************************************************/ +/** */ +/** TX_THREAD *_tx_misra_void_to_thread_pointer_convert(void *pointer); */ +/** */ +/**********************************************************************************/ +/**********************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_void_to_thread_pointer_convert: + BX LR ;; return + + +#ifdef TX_ENABLE_EVENT_TRACE + +/************************************************************************************************/ +/************************************************************************************************/ +/** */ +/** UCHAR *_tx_misra_object_to_uchar_pointer_convert(TX_TRACE_OBJECT_ENTRY *pointer); */ +/** */ +/************************************************************************************************/ +/************************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_object_to_uchar_pointer_convert: + BX LR ;; return + + +/************************************************************************************************/ +/************************************************************************************************/ +/** */ +/** TX_TRACE_OBJECT_ENTRY *_tx_misra_uchar_to_object_pointer_convert(UCHAR *pointer); */ +/** */ +/************************************************************************************************/ +/************************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_object_pointer_convert: + BX LR ;; return + + +/******************************************************************************************/ +/******************************************************************************************/ +/** */ +/** TX_TRACE_HEADER *_tx_misra_uchar_to_header_pointer_convert(UCHAR *pointer); */ +/** */ +/******************************************************************************************/ +/******************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_header_pointer_convert: + BX LR ;; return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** TX_TRACE_BUFFER_ENTRY *_tx_misra_uchar_to_entry_pointer_convert(UCHAR *pointer); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_uchar_to_entry_pointer_convert: + BX LR ;; return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** UCHAR *_tx_misra_entry_to_uchar_pointer_convert(TX_TRACE_BUFFER_ENTRY *pointer); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_entry_to_uchar_pointer_convert: + BX LR ;; return +#endif + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** UCHAR *_tx_misra_char_to_uchar_pointer_convert(CHAR *pointer); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_char_to_uchar_pointer_convert: + BX LR ;; return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_ipsr_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_ipsr_get: + MRS R0, IPSR + BX LR ;; return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_control_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_control_get: + MRS R0, CONTROL + BX LR ;; return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** void _tx_misra_control_set(ULONG value); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_control_set: + MSR CONTROL, R0 + BX LR ;; return + + +#ifdef __ARMVFP__ + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_fpccr_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +_tx_misra_fpccr_get: + LDR r0, =0xE000EF34 ; Build FPCCR address + LDR r0, [r0] ; Load FPCCR value + BX LR ;; return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** void _tx_misra_vfp_touch(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + SECTION `.text`:CODE:NOROOT(1) + THUMB +_tx_misra_vfp_touch: + vmov.f32 s0, s0 + BX LR ;; return + +#endif + + + SECTION `.iar_vfe_header`:DATA:NOALLOC:NOROOT(2) + SECTION_TYPE SHT_PROGBITS, 0 + DATA + DC32 0 + + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_restore.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_restore.S new file mode 100644 index 00000000..ba435b45 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_restore.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + EXTERN _tx_execution_isr_exit + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_exit] Execution profiling ISR exit */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + PUBLIC _tx_thread_context_restore +_tx_thread_context_restore: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR exit function to indicate an ISR is complete. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_exit // Call the ISR exit function + POP {r0, r1} // Recover return address + MOV lr, r1 +#endif + + BX lr +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_save.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_save.S new file mode 100644 index 00000000..c3adc095 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_context_save.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + EXTERN _tx_execution_isr_enter + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_enter] Execution profiling ISR enter */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + PUBLIC _tx_thread_context_save +_tx_thread_context_save: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR enter function to indicate an ISR is starting. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_enter // Call the ISR enter function + POP {r0, r1} // Recover return address + MOV lr, r1 +#endif + + /* Context is already saved - just return. */ + + BX lr +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_control.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..5be18f72 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_control.S @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + PUBLIC _tx_thread_interrupt_control +_tx_thread_interrupt_control: + +/* Pickup current interrupt lockout posture. */ + + MRS r1, PRIMASK // Pickup current interrupt lockout + + +/* Apply the new interrupt posture. */ + + MSR PRIMASK, r0 // Apply the new interrupt lockout + MOV r0, r1 // Transfer old to return register + BX lr // Return to caller + +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_disable.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..7ef52d56 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_disable.S @@ -0,0 +1,75 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts and returning */ +/* the previous interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(VOID) +// { + PUBLIC _tx_thread_interrupt_disable +_tx_thread_interrupt_disable: + +/* Pickup current interrupt lockout posture. */ + + MRS r0, PRIMASK + CPSID i + BX lr + +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_restore.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..a6aa4ca4 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_interrupt_restore.S @@ -0,0 +1,71 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring the previous */ +/* interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* previous_posture Previous interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_interrupt_restore(UINT previous_posture) +// { + PUBLIC _tx_thread_interrupt_restore +_tx_thread_interrupt_restore: + /* Restore previous interrupt lockout posture. */ + MSR PRIMASK, r0 + BX lr +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_schedule.S new file mode 100644 index 00000000..c75596df --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_schedule.S @@ -0,0 +1,548 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + EXTERN _tx_thread_current_ptr + EXTERN _tx_thread_execute_ptr + EXTERN _tx_timer_time_slice + EXTERN _tx_execution_thread_enter + EXTERN _tx_execution_thread_exit + EXTERN _tx_thread_preempt_disable + EXTERN _txm_module_manager_memory_fault_handler + EXTERN _txm_module_manager_memory_fault_info + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + PUBLIC _tx_thread_schedule +_tx_thread_schedule: + /* This function should only ever be called on Cortex-M + from the first schedule request. Subsequent scheduling occurs + from the PendSV handling routine below. */ + + /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets. */ + MOVS r0, #0 // Build value for TX_FALSE + LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag + STR r0, [r2, #0] // Clear preempt disable flag + + /* Enable interrupts */ + CPSIE i + + /* Enter the scheduler for the first time. */ + LDR r0, =0x10000000 // Load PENDSVSET bit + LDR r1, =0xE000ED04 // Load ICSR address + STR r0, [r1] // Set PENDSVBIT in ICSR + DSB // Complete all memory accesses + ISB // Flush pipeline + + /* Wait here for the PendSV to take place. */ + +__tx_wait_here: + B __tx_wait_here // Wait for the PendSV to happen +// } + + + /* Memory Exception Handler. */ + + PUBLIC MemManage_Handler + PUBLIC BusFault_Handler + PUBLIC UsageFault_Handler +MemManage_Handler: +BusFault_Handler: +UsageFault_Handler: + + CPSID i // Disable interrupts + + /* Now pickup and store all the fault related information. */ + + LDR r2,=_txm_module_manager_memory_fault_info // Pickup fault info struct + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + STR r1, [r2, #0] // Save current thread pointer in fault info structure + MRS r0, CONTROL // Pickup current CONTROL register + STR r0, [r2, #24] // Save CONTROL + MRS r1, PSP // Pickup thread stack pointer + STR r1, [r2, #28] // Save thread stack pointer + LDR r0, [r1] // Pickup saved r0 (as r0-r3, r12, lr, pc, xpsr are automatically saved to stack) + STR r0, [r2, #32] // Save r0 + LDR r0, [r1, #4] // Pickup saved r1 + STR r0, [r2, #36] // Save r1 + LDR r0, [r1, #8] // Pickup saved r2 + STR r0, [r2, #40] // Save r2 + STR r3, [r2, #44] // Save r3 + STR r4, [r2, #48] // Save r4 + STR r5, [r2, #52] // Save r5 + STR r6, [r2, #56] // Save r6 + STR r7, [r2, #60] // Save r7 + MOV r0, r8 // Pickup current r8 register (high register) + STR r0, [r2, #64] // Save r8 + MOV r0, r9 // Pickup current r9 register + STR r0, [r2, #68] // Save r9 + MOV r0, r10 // Pickup current r10 register + STR r0, [r2, #72] // Save r10 + MOV r0, r11 // Pickup current r11 register + STR r0, [r2, #76] // Save r11 + LDR r0, [r1, #16] // Pickup saved r12 + STR r0, [r2, #80] // Save r12 + LDR r0, [r1, #20] // Pickup saved lr + STR r0, [r2, #84] // Save lr + LDR r0, [r1, #24] // Pickup instruction address at point of fault + STR r0, [r2, #4] // Save point of fault + LDR r0, [r1, #28] // Pickup xPSR + STR r0, [r2, #88] // Save xPSR + + MRS r0, CONTROL // Pickup current CONTROL register + MOVS r1, #1 + BICS r0, r0, r1 // Clear the UNPRIV bit + MSR CONTROL, r0 // Setup new CONTROL register + + BL _txm_module_manager_memory_fault_handler // Call memory manager fault handler + +#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. */ + CPSID i // Disable interrupts + BL _tx_execution_thread_exit // Call the thread exit function + CPSIE i // Enable interrupts +#endif + + MOVS 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 MemManage_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 + LDR r0, =0xFFFFFFFD // Exception return + MOV lr, r0 // Move exception return to lr + BX lr // Return from exception + + + /* Generic context switching PendSV handler. */ + + PUBLIC PendSV_Handler + PUBLIC __tx_PendSVHandler +PendSV_Handler: +__tx_PendSVHandler: + + /* Get current thread value and new thread pointer. */ + +__tx_ts_handler: + +#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. */ + CPSID i // Disable interrupts + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, r1} // Recover LR + MOV lr, r1 // + CPSIE i // Enable interrupts +#endif + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + MOVS r3, #0 // Build NULL value + LDR r1, [r0] // Pickup current thread pointer + + /* Determine if there is a current thread to finish preserving. */ + CMP r1, #0 + BEQ __tx_ts_new // If NULL, skip preservation + + /* Recover PSP and preserve current thread context. */ + + STR r3, [r0] // Set _tx_thread_current_ptr to NULL + MRS r3, PSP // Pickup PSP pointer (thread's stack pointer) + SUBS r3, r3, #16 // Allocate stack space + STM r3!, {r4-r7} // Save its remaining registers + MOV r4, r8 // Pick up r8 + MOV r5, r9 // Pick up r9 + MOV r6, r10 // Pick up r10 + MOV r7, r11 // Pick up r11 + SUBS r3, r3, #32 // Allocate stack space for r8-r11 + STM r3!, {r4-r7} // Save r8-r11 + SUBS r3, r3, #20 // Allocate stack space for lr + MOV r5, lr // Move lr into r5 + STR r5, [r3] // Save lr + STR r3, [r1, #8] // Save the thread stack pointer + + /* Determine if time-slice is active. If it isn't, skip time handling processing. */ + + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r4] // Pickup current time-slice + CMP r5, #0 + BEQ __tx_ts_new // If not active, skip processing + + /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable. */ + + STR r5, [r1, #24] // Save current time-slice + + /* Clear the global time-slice. */ + MOVS r3, #0 // Build NULL value + STR r3, [r4] // Clear time-slice + + /* Executing thread is now completely preserved!!! */ + +__tx_ts_new: + + /* Now we are looking for a new thread to execute! */ + + CPSID i // Disable interrupts + LDR r1, [r2] // Is there another thread ready to execute? + CMP r1, #0 + BNE __tx_ts_restore // Yes, schedule it + + /* The following is the idle wait processing... in this case, no threads are ready for execution and the + system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts + are disabled to allow use of WFI for waiting for a thread to arrive. */ + +__tx_ts_wait: + CPSID i // Disable interrupts + LDR r1, [r2] // Pickup the next thread to execute pointer + CMP r1, #0 + BNE __tx_ts_ready // If non-NULL, a new thread is ready! +#ifdef TX_ENABLE_WFI + DSB // Ensure no outstanding memory transactions + WFI // Wait for interrupt + ISB // Ensure pipeline is flushed +#endif + CPSIE i // Enable interrupts + B __tx_ts_wait // Loop to continue waiting + + /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are + already in the handler! */ + +__tx_ts_ready: + LDR r7, =0x08000000 // Build clear PendSV value + LDR r6, =0xE000ED04 // Build base NVIC address + STR r7, [r6] // Clear any PendSV + +__tx_ts_restore: + + /* A thread is ready, make the current thread the new thread + and enable interrupts. */ + + STR r1, [r0] // Setup the current thread pointer to the new thread + CPSIE i // Enable interrupts + + /* Increment the thread run count. */ + + LDR r7, [r1, #4] // Pickup the current thread run count + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r1, #24] // Pickup thread's current time-slice + ADDS r7, r7, #1 // Increment the thread run count + STR r7, [r1, #4] // Store the new run count + + /* Setup global time-slice with thread's current time-slice. */ + + STR r5, [r4] // Setup global time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread entry function to indicate the thread is executing. */ + PUSH {r0, r1} // Save r0 and r1 + BL _tx_execution_thread_enter // Call the thread execution enter function + POP {r0, r1} // Recover r0 and r1 +#endif + + /* Restore the thread context and PSP. */ + + LDR r3, [r1, #8] // Pickup thread's stack pointer + + // Set up CONTROL register based on user mode flag (privileged/unprivileged mode) + MRS r5, CONTROL // Pickup current CONTROL register + MOVS r6, #0x98 + LDR r4, [r1, r6] // Pickup current user mode flag + MOVS r6, #1 + BICS r5, r5, r6 // Clear the UNPRIV bit + ORRS r4, r4, r5 // Build new CONTROL register + MSR CONTROL, r4 // Setup new CONTROL register + + // Determine if MPU needs to be configured + LDR r0, =0xE000ED94 // Build MPU control reg address + MOVS r6, #0 // Build disable value + STR r6, [r0] // Disable MPU + MOVS r6, #0x90 + LDR r0, [r1, r6] // Pickup the module instance pointer + CMP r0, #0 + BEQ skip_mpu_setup // Is this thread owned by a module? No, skip MPU setup + MOV r8, r1 // Copy thread ptr + LDR r1, [r0, #0x64] // Pickup MPU register[0] + CMP r1, #0 + BEQ skip_mpu_setup // Is protection required for this module? No, skip MPU setup + LDR r1, =0xE000ED9C // Build address of MPU base register + + // Initialize loop to configure MPU registers + // Order doesn't matter, so txm_module_instance_mpu_registers[0] + // will be in region 7 and txm_module_instance_mpu_registers[7] will be in region 0. + MOVS r3, #0x64 // Index of MPU register settings in thread control block + ADD r0, r0, r3 // Build address of MPU register start in thread control block + MOVS r5, #0 // Select region 0 + LDR r4, =0xE000ED98 // Region register address + // Loop to load MPU registers +_tx_mpu_loop: + LDR r1, =0xE000ED9C // Build address of MPU base register + STR r5, [r4] // Set region + LDM r0!, {r2-r3} // Get MPU settings from the module + STM r1!, {r2-r3} // Set MPU registers for region + ADDS r5, r5, #1 // Increment to next region + CMP r5, #8 // Check if all regions have been set + BNE _tx_mpu_loop + + LDR r0, =0xE000ED94 // Build MPU control reg address + MOVS r1, #5 // Build enable value with background region enabled + STR r1, [r0] // Enable MPU + MOV r1, r8 // Get copied thread ptr + +skip_mpu_setup: + + // Restore the thread context and PSP + LDR r3, [r1, #8] // Pickup thread's stack pointer + LDR r5, [r3] // Recover saved LR + ADDS r3, r3, #4 // Position past LR + MOV lr, r5 // Restore LR + LDM r3!, {r4-r7} // Recover thread's registers (r8-r11) + MOV r11, r7 + MOV r10, r6 + MOV r9, r5 + MOV r8, r4 + LDM r3!, {r4-r7} // Recover thread's registers (r4-r7) + MSR PSP, r3 // Setup the thread's stack pointer + + BX lr // Return to thread! + + + /* SVC Handler. */ + + PUBLIC SVC_Handler + PUBLIC __tx_SVCallHandler +SVC_Handler: +__tx_SVCallHandler: + + MRS r0, PSP // Pickup the PSP stack + LDR r1, [r0, #24] // Pickup the point of interrupt + MOV r3, r1 + SUBS r3, r3, #2 + LDRB r2, [r3] // Pickup the SVC parameter + + /* Determine which SVC trap we are processing */ + + CMP r2, #1 // Is it the entry into ThreadX? + BNE _tx_thread_user_return // No, return to user mode + + /* At this point we have an SVC 1, which means we are entering + the kernel from a module thread with user mode selected. */ + + LDR r2, =_txm_module_priv // Load address of where we should have come from + SUBS r2, r2, #1 // Subtract 1 because of THUMB mode. + CMP r1, r2 // Did we come from user_mode_entry? + BEQ _tx_entry_continue // If no (not equal), then... + BX lr // return from where we came. +_tx_entry_continue: + LDR r3, [r0, #20] // This is the saved LR + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0 // Build clear value + MOVS r0, #0x98 // Index of current user mode + STR r1, [r2, r0] // Clear the current user mode selection for thread + MOVS r0, #0xA0 // Index of saved LR + STR r3, [r2, r0] // Save the original LR in thread control block + + /* If there is memory protection, use kernel stack */ + MOVS r0, #0x90 // Index of module instance ptr + LDR r0, [r2, r0] // Load the module instance ptr + LDR r0, [r0, #0x0C] // Load the module property flags + MOVS r1, #2 // MPU protection flag + TST r0, r1 // Check if memory protected + BEQ _tx_skip_kernel_stack_enter + + /* Switch to the module thread's kernel stack */ + MOVS r0, #0xA8 // Index of module kernel stack end + LDR r0, [r2, r0] // Load the module kernel stack end + MOVS r1, #0xA4 // Index of module kernel stack start + LDR r1, [r2, r1] // Load the module kernel stack start + +#ifndef TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE + MOVS r3, #0xAC // Index of module kernel stack size + LDR r3, [r2, r3] // Load the module kernel stack size + STR r1, [r2, #12] // Set stack start + STR r0, [r2, #16] // Set stack end + STR r3, [r2, #20] // Set stack size +#endif + + MRS r3, PSP // Pickup thread stack pointer + MOVS r1, #0xB0 // Index of module stack pointer + STR r3, [r2, r1] // Save thread stack pointer + + /* Build kernel stack by copying thread stack two registers at a time */ + SUBS r0, r0, #32 // Start at top of hardware stack + LDMIA r3!, {r1,r2} // Get r0, r1 from thread stack + STMIA r0!, {r1,r2} // Insert r0, r1 into kernel stack + LDMIA r3!, {r1,r2} // Get r2, r3 from thread stack + STMIA r0!, {r1,r2} // Insert r2, r3 into kernel stack + LDMIA r3!, {r1,r2} // Get r12, lr from thread stack + STMIA r0!, {r1,r2} // Insert r12, lr into kernel stack + LDMIA r3!, {r1,r2} // Get pc, xpsr from thread stack + STMIA r0!, {r1,r2} // Insert pc, xpsr into kernel stack + SUBS r0, r0, #32 // Go back to top of stack + + MSR PSP, r0 // Set kernel stack pointer + +_tx_skip_kernel_stack_enter: + MRS r0, CONTROL // Pickup current CONTROL register + MOVS r1, #1 + BICS r0, r0, r1 // Clear the UNPRIV bit + MSR CONTROL, r0 // Setup new CONTROL register + BX lr // Return to thread + +_tx_thread_user_return: + LDR r2, =_txm_module_user_mode_exit // Load address of where we should have come from + SUBS r2, r2, #1 // Subtract 1 because of THUMB mode. + CMP r1, r2 // Did we come from user_mode_exit? + BEQ _tx_exit_continue // If no (not equal), then... + BX lr // return from where we came. +_tx_exit_continue: + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0x9C // Index of user mode + MOVS r3, #0x98 // Index of current user mode + LDR r1, [r2, r1] // Pick up user mode + STR r1, [r2, r3] // Set the current user mode selection for thread + + /* If there is memory protection, use kernel stack */ + MOVS r0, #0x90 // Index of module instance ptr + LDR r0, [r2, r0] // Load the module instance ptr + LDR r0, [r0, #0x0C] // Load the module property flags + MOVS r1, #2 // MPU protection flag + TST r0, r1 // Check if memory protected + BEQ _tx_skip_kernel_stack_exit + +#ifndef TXM_MODULE_KERNEL_STACK_MAINTENANCE_DISABLE + MOVS r0, #0xB4 // Index of module thread stack start + LDR r0, [r2, r0] // Load the module thread stack start + MOVS r1, #0xB8 // Index of module thread stack end + LDR r1, [r2, r1] // Load the module thread stack end + MOVS r3, #0xBC // Index of module thread stack size + LDR r3, [r2, r3] // Load the module thread stack size + STR r0, [r2, #12] // Set stack start + STR r1, [r2, #16] // Set stack end + STR r3, [r2, #20] // Set stack size +#endif + MOVS r1, #0xB0 // Index of module thread stack pointer + LDR r0, [r2, r1] // Load the module thread stack pointer + MRS r3, PSP // Pickup kernel stack pointer + + /* Copy kernel hardware stack to module thread stack. */ + LDM r3!, {r1-r2} // Get r0, r1 from kernel stack + STM r0!, {r1-r2} // Insert r0, r1 into thread stack + LDM r3!, {r1-r2} // Get r2, r3 from kernel stack + STM r0!, {r1-r2} // Insert r2, r3 into thread stack + LDM r3!, {r1-r2} // Get r12, lr from kernel stack + STM r0!, {r1-r2} // Insert r12, lr into thread stack + LDM r3!, {r1-r2} // Get pc, xpsr from kernel stack + STM r0!, {r1-r2} // Insert pc, xpsr into thread stack + SUBS r0, r0, #32 // Subtract 32 to get back to top of stack + MSR PSP, r0 // Set thread stack pointer + + LDR r1, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, [r1] // Pickup current thread pointer + MOVS r1, #0x9C // Index of user mode + LDR r1, [r2, r1] // Pick up user mode + +_tx_skip_kernel_stack_exit: + MRS r0, CONTROL // Pickup current CONTROL register + ORRS r0, r0, r1 // OR in the user mode bit + MSR CONTROL, r0 // Setup new CONTROL register + BX lr // Return to thread + + /* Kernel entry function from user mode. */ + + EXTERN _txm_module_manager_kernel_dispatch + SECTION `.text`:CODE:NOROOT(5) + THUMB + ALIGNROM 5 +// VOID _txm_module_manager_user_mode_entry(VOID) +// { + PUBLIC _txm_module_manager_user_mode_entry +_txm_module_manager_user_mode_entry: + SVC 1 // Enter kernel +_txm_module_priv: + /* At this point, we are out of user mode. The original LR has been saved in the + thread control block. Simply call the kernel dispatch function. */ + BL _txm_module_manager_kernel_dispatch + + /* Pickup the original LR value while still in privileged mode */ + LDR r2, =_tx_thread_current_ptr // Build current thread pointer address + LDR r3, [r2] // Pickup current thread pointer + MOVS r2, #0xA0 + LDR r1, [r3, r2] // Pickup saved LR from original call + MOV lr, r1 + + SVC 2 // Exit kernel and return to user mode +_txm_module_user_mode_exit: + BX lr // Return to the caller + NOP + NOP + NOP + NOP +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_stack_build.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_stack_build.S new file mode 100644 index 00000000..bcd90c5a --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_stack_build.S @@ -0,0 +1,133 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + PUBLIC _tx_thread_stack_build +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + MOVS r3, #0x7 + BICS r2, r2, r3 // Align frame for 8-byte alignment + SUBS r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOVS r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r4 + STR r3, [r2, #8] // Store initial r5 + STR r3, [r2, #12] // Store initial r6 + STR r3, [r2, #16] // Store initial r7 + STR r3, [r2, #20] // Store initial r8 + STR r3, [r2, #24] // Store initial r9 + STR r3, [r2, #28] // Store initial r10 + STR r3, [r2, #32] // Store initial r11 + + /* Hardware stack follows. */ + + STR r3, [r2, #36] // Store initial r0 + STR r3, [r2, #40] // Store initial r1 + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + LDR r3, =0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + LDR r3, =0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's + // control block + BX lr // Return to caller +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_system_return.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_system_return.S new file mode 100644 index 00000000..91632ba0 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_thread_system_return.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + PUBLIC _tx_thread_system_return +_tx_thread_system_return: + + /* Return to real scheduler via PendSV. Note that this routine is often + replaced with in-line assembly in tx_port.h to improved performance. */ + + LDR r0, =0x10000000 // Load PENDSVSET bit + LDR r1, =0xE000ED04 // Load NVIC base + STR r0, [r1] // Set PENDSVBIT in ICSR + MRS r0, IPSR // Pickup IPSR + CMP r0, #0 // Is it a thread returning? + BNE _isr_context // If ISR, skip interrupt enable + MRS r1, PRIMASK // Thread context returning, pickup PRIMASK + CPSIE i // Enable interrupts + MSR PRIMASK, r1 // Restore original interrupt posture +_isr_context: + BX lr // Return to caller +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m0+/iar/module_manager/src/tx_timer_interrupt.S new file mode 100644 index 00000000..7ac098b5 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/tx_timer_interrupt.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + EXTERN _tx_timer_time_slice + EXTERN _tx_timer_system_clock + EXTERN _tx_timer_current_ptr + EXTERN _tx_timer_list_start + EXTERN _tx_timer_list_end + EXTERN _tx_timer_expired_time_slice + EXTERN _tx_timer_expired + EXTERN _tx_thread_time_slice + EXTERN _tx_timer_expiration_process + EXTERN _tx_thread_current_ptr + EXTERN _tx_thread_execute_ptr + EXTERN _tx_thread_preempt_disable + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* expiration functions are called. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + PUBLIC _tx_timer_interrupt +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR r1, =_tx_timer_system_clock // Pickup address of system clock + LDR r0, [r1, #0] // Pickup system clock + ADDS r0, r0, #1 // Increment system clock + STR r0, [r1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + // if (_tx_timer_time_slice) + // { + + LDR r3, =_tx_timer_time_slice // Pickup address of time-slice + LDR r2, [r3, #0] // Pickup time-slice + CMP r2, #0 // Is it non-active? + BEQ __tx_timer_no_time_slice // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + // _tx_timer_time_slice--; + + SUBS r2, r2, #1 // Decrement the time-slice + STR r2, [r3, #0] // Store new time-slice value + + /* Check for expiration. */ + // if (__tx_timer_time_slice == 0) + + CMP r2, #0 // Has it expired? + BNE __tx_timer_no_time_slice // No, skip expiration processing + + /* Set the time-slice expired flag. */ + // _tx_timer_expired_time_slice = TX_TRUE; + + LDR r3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOVS r0, #1 // Build expired value + STR r0, [r3, #0] // Set time-slice expiration flag + + // } + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR r1, =_tx_timer_current_ptr // Pickup current timer pointer address + LDR r0, [r1, #0] // Pickup current timer + LDR r2, [r0, #0] // Pickup timer list entry + CMP r2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR r3, =_tx_timer_expired // Pickup expiration flag address + MOVS r2, #1 // Build expired value + STR r2, [r3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADDS r0, r0, #4 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR r3, =_tx_timer_list_end // Pickup addr of timer list end + LDR r2, [r3, #0] // Pickup list end + CMP r0, r2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR r3, =_tx_timer_list_start // Pickup addr of timer list start + LDR r0, [r3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR r0, [r1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR r2, [r3, #0] // Pickup time-slice expired flag + CMP r2, #0 // Did a time-slice expire? + BNE __tx_something_expired // If non-zero, time-slice expired + LDR r1, =_tx_timer_expired // Pickup addr of other expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CMP r0, #0 // Did a timer expire? + BEQ __tx_timer_nothing_expired // No, nothing expired + +__tx_something_expired: + + + PUSH {r0, lr} // Save the lr register on the stack + // and save r0 just to keep 8-byte alignment + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR r1, =_tx_timer_expired // Pickup addr of expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CMP r0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR r2, [r3, #0] // Pickup the actual flag + CMP r2, #0 // See if the flag is set + BEQ __tx_timer_not_ts_expiration // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + LDR r0, =_tx_thread_preempt_disable // Build address of preempt disable flag + LDR r1, [r0] // Is the preempt disable flag set? + CMP r1, #0 // + BNE __tx_timer_skip_time_slice // Yes, skip the PendSV logic + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + LDR r3, [r2] // Pickup the execute thread pointer + LDR r0, =0xE000ED04 // Build address of control register + LDR r2, =0x10000000 // Build value for PendSV bit + CMP r1, r3 // Are they the same? + BEQ __tx_timer_skip_time_slice // If the same, there was no time-slice performed + STR r2, [r0] // Not the same, issue the PendSV for preemption +__tx_timer_skip_time_slice: + + // } + +__tx_timer_not_ts_expiration: + + POP {r0, r1} // Recover lr register (r0 is just there for + MOV lr, r1 // the 8-byte stack alignment + + // } + +__tx_timer_nothing_expired: + + DSB // Complete all memory access + BX lr // Return to caller +// } + END diff --git a/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_alignment_adjust.c b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_alignment_adjust.c new file mode 100644 index 00000000..6c8b3b57 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_alignment_adjust.c @@ -0,0 +1,448 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_power_of_two_block_size Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates a power of two size at or immediately above*/ +/* the input size and returns it to the caller. */ +/* */ +/* INPUT */ +/* */ +/* size Block size */ +/* */ +/* OUTPUT */ +/* */ +/* calculated size Rounded up to power of two */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_alignment_adjust Adjust alignment for Cortex-M */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_power_of_two_block_size(ULONG size) +{ + /* Check for 0 size. */ + if(size == 0) + return 0; + + /* Minimum MPU block size is 256. */ + if(size <= 256) + return 256; + + /* Bit twiddling trick to round to next high power of 2 + (if original size is power of 2, it will return original size. Perfect!) */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + /* Return a power of 2 size at or above the input size. */ + return(size); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_alignment_adjust Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function adjusts the alignment and size of the code and data */ +/* section for a given module implementation. */ +/* */ +/* INPUT */ +/* */ +/* module_preamble Pointer to module preamble */ +/* code_size Size of the code area (updated) */ +/* code_alignment Code area alignment (updated) */ +/* data_size Size of data area (updated) */ +/* data_alignment Data area alignment (updated) */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _txm_power_of_two_block_size Calculate power of two size */ +/* */ +/* CALLED BY */ +/* */ +/* Initial thread stack frame */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, + ULONG *code_size, + ULONG *code_alignment, + ULONG *data_size, + ULONG *data_alignment) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU +ULONG local_code_size; +ULONG local_code_alignment; +ULONG local_data_size; +ULONG local_data_alignment; +ULONG code_size_accum; +ULONG data_size_accum; + + /* Copy the input parameters into local variables for ease of use. */ + local_code_size = *code_size; + local_code_alignment = *code_alignment; + local_data_size = *data_size; + local_data_alignment = *data_alignment; + + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_code_alignment = _txm_power_of_two_block_size(local_code_size) >> 2; + code_size_accum = local_code_alignment + local_code_alignment; + code_size_accum = code_size_accum + (_txm_power_of_two_block_size(local_code_size - code_size_accum) >> 1); + code_size_accum = code_size_accum + _txm_power_of_two_block_size(local_code_size - code_size_accum); + local_code_size = code_size_accum; + + /* Determine data block sizes. Minimize the alignment requirement. + There are 4 MPU data entries available. The following is how the data size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to data size. + 2. 1/4 of the largest power of two that is greater than or equal to data size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_data_alignment = _txm_power_of_two_block_size(local_data_size) >> 2; + data_size_accum = local_data_alignment + local_data_alignment; + data_size_accum = data_size_accum + (_txm_power_of_two_block_size(local_data_size - data_size_accum) >> 1); + data_size_accum = data_size_accum + _txm_power_of_two_block_size(local_data_size - data_size_accum); + local_data_size = data_size_accum; + + /* Return all the information to the caller. */ + *code_size = local_code_size; + *code_alignment = local_code_alignment; + *data_size = local_data_size; + *data_alignment = local_data_alignment; + +#else + +ULONG local_code_size; +ULONG local_code_alignment; +ULONG local_data_size; +ULONG local_data_alignment; +ULONG code_block_size; +ULONG data_block_size; +ULONG code_size_accum; +ULONG data_size_accum; + + /* Copy the input parameters into local variables for ease of use. */ + local_code_size = *code_size; + local_code_alignment = *code_alignment; + local_data_size = *data_size; + local_data_alignment = *data_alignment; + + + /* Test for external memory enabled in preamble. */ + if(module_preamble -> txm_module_preamble_property_flags & TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS) + { + /* External/shared memory enabled. TXM_MODULE_MANAGER_CODE_MPU_ENTRIES-1 code entries will be used. */ + if (local_code_size <= (32*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 32 is best. */ + code_block_size = 32; + } + else if (local_code_size <= (64*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 64 is best. */ + code_block_size = 64; + } + else if (local_code_size <= (128*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 128 is best. */ + code_block_size = 128; + } + else if (local_code_size <= (256*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 256 is best. */ + code_block_size = 256; + } + else if (local_code_size <= (512*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 512 is best. */ + code_block_size = 512; + } + else if (local_code_size <= (1024*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 1024 is best. */ + code_block_size = 1024; + } + else if (local_code_size <= (2048*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 2048 is best. */ + code_block_size = 2048; + } + else if (local_code_size <= (4096*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 4096 is best. */ + code_block_size = 4096; + } + else if (local_code_size <= (8192*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 8192 is best. */ + code_block_size = 8192; + } + else if (local_code_size <= (16384*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 16384 is best. */ + code_block_size = 16384; + } + else if (local_code_size <= (32768*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 32768 is best. */ + code_block_size = 32768; + } + else if (local_code_size <= (65536*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 65536 is best. */ + code_block_size = 65536; + } + else if (local_code_size <= (131072*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 131072 is best. */ + code_block_size = 131072; + } + else if (local_code_size <= (262144*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 262144 is best. */ + code_block_size = 262144; + } + else if (local_code_size <= (524288*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 524288 is best. */ + code_block_size = 524288; + } + else if (local_code_size <= (1048576*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 1048576 is best. */ + code_block_size = 1048576; + } + else if (local_code_size <= (2097152*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 2097152 is best. */ + code_block_size = 2097152; + } + else if (local_code_size <= (4194304*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1))) + { + /* Block size of 4194304 is best. */ + code_block_size = 4194304; + } + else + { + /* Just set block size to 32MB just to create an allocation error! */ + code_block_size = 33554432; + } + + /* Calculate the new code size. */ + local_code_size = code_block_size*(TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1); + + /* Determine if the code block size is greater than the current alignment. If so, use block size + as the alignment. */ + if (code_block_size > local_code_alignment) + local_code_alignment = code_block_size; + + } + else + { + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 32. */ + local_code_alignment = _txm_power_of_two_block_size(local_code_size) >> 2; + code_size_accum = local_code_alignment + local_code_alignment; + code_size_accum = code_size_accum + (_txm_power_of_two_block_size(local_code_size - code_size_accum) >> 1); + code_size_accum = code_size_accum + _txm_power_of_two_block_size(local_code_size - code_size_accum); + local_code_size = code_size_accum; + } + + /* Determine the best data block size, which in our case is the minimal alignment. */ + if (local_data_size <= (32*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 32 is best. */ + data_block_size = 32; + } + else if (local_data_size <= (64*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 64 is best. */ + data_block_size = 64; + } + else if (local_data_size <= (128*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 128 is best. */ + data_block_size = 128; + } + else if (local_data_size <= (256*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 256 is best. */ + data_block_size = 256; + } + else if (local_data_size <= (512*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 512 is best. */ + data_block_size = 512; + } + else if (local_data_size <= (1024*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 1024 is best. */ + data_block_size = 1024; + } + else if (local_data_size <= (2048*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 2048 is best. */ + data_block_size = 2048; + } + else if (local_data_size <= (4096*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 4096 is best. */ + data_block_size = 4096; + } + else if (local_data_size <= (8192*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 8192 is best. */ + data_block_size = 8192; + } + else if (local_data_size <= (16384*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 16384 is best. */ + data_block_size = 16384; + } + else if (local_data_size <= (32768*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 32768 is best. */ + data_block_size = 32768; + } + else if (local_data_size <= (65536*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 65536 is best. */ + data_block_size = 65536; + } + else if (local_data_size <= (131072*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 131072 is best. */ + data_block_size = 131072; + } + else if (local_data_size <= (262144*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 262144 is best. */ + data_block_size = 262144; + } + else if (local_data_size <= (524288*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 524288 is best. */ + data_block_size = 524288; + } + else if (local_data_size <= (1048576*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 1048576 is best. */ + data_block_size = 1048576; + } + else if (local_data_size <= (2097152*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 2097152 is best. */ + data_block_size = 2097152; + } + else if (local_data_size <= (4194304*TXM_MODULE_MANAGER_DATA_MPU_ENTRIES)) + { + /* Block size of 4194304 is best. */ + data_block_size = 4194304; + } + else + { + /* Just set data block size to 32MB just to create an allocation error! */ + data_block_size = 33554432; + } + + /* Calculate the new data size. */ + data_size_accum = data_block_size; + while(data_size_accum < local_data_size) + { + data_size_accum += data_block_size; + } + local_data_size = data_size_accum; + + /* Determine if the data block size is greater than the current alignment. If so, use block size + as the alignment. */ + if (data_block_size > local_data_alignment) + { + local_data_alignment = data_block_size; + } + + /* Return all the information to the caller. */ + *code_size = local_code_size; + *code_alignment = local_code_alignment; + *data_size = local_data_size; + *data_alignment = local_data_alignment; + +#endif +} diff --git a/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_external_memory_enable.c b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_external_memory_enable.c new file mode 100644 index 00000000..1386362a --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_external_memory_enable.c @@ -0,0 +1,295 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_mutex.h" +#include "tx_queue.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_external_memory_enable Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function creates an entry in the MPU table for a shared */ +/* memory space. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Module instance pointer */ +/* start_address Start address of memory */ +/* length Length of external memory */ +/* attributes Memory attributes (r/w) */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* _tx_mutex_get Get protection mutex */ +/* _tx_mutex_put Release protection mutex */ +/* _txm_power_of_two_block_size Round length to power of two */ +/* _txm_module_manager_mm_register_setup Reconfigure MPU registers */ +/* */ +/* CALLED BY */ +/* */ +/* Application code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_external_memory_enable(TXM_MODULE_INSTANCE *module_instance, + VOID *start_address, + ULONG length, + UINT attributes) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU +ULONG block_size; +ULONG region_size; +ULONG srd_bits; +ULONG size_register; +ULONG address; +ULONG shared_index; +ULONG attributes_check = 0; + + /* Determine if the module manager has not been initialized yet. */ + if (_txm_module_manager_ready != TX_TRUE) + { + /* Module manager has not been initialized. */ + return(TX_NOT_AVAILABLE); + } + + /* Determine if the module is valid. */ + if (module_instance == TX_NULL) + { + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Get module manager protection mutex. */ + _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); + + /* Determine if the module instance is valid. */ + if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Determine if the module instance is in the loaded state. */ + if (module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if the module is not ready. */ + return(TX_START_ERROR); + } + + /* Determine if there are shared memory entries available. */ + if(module_instance -> txm_module_instance_shared_memory_count >= TXM_MODULE_MPU_SHARED_ENTRIES) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* No more entries available. */ + return(TX_NO_MEMORY); + } + + /* Start address and length must adhere to Cortex-M7 MPU. + The address must align with the block size. */ + + block_size = _txm_power_of_two_block_size(length); + address = (ULONG) start_address; + if(address != (address & ~(block_size - 1))) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return alignment error. */ + return(TXM_MODULE_ALIGNMENT_ERROR); + } + + /* At this point, we have a valid address and block size. + Set up MPU registers. */ + + /* Pick up index into shared memory entries. */ + shared_index = TXM_MODULE_MPU_SHARED_INDEX + module_instance -> txm_module_instance_shared_memory_count; + + /* Save address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[shared_index].txm_module_mpu_region_address = address | shared_index | 0x10; + + /* Calculate the region size. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Calculate the subregion bits. */ + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, length); + + /* Generate SRD, size, and enable attributes. */ + size_register = srd_bits | region_size | TXM_ENABLE_REGION | TXM_MODULE_MPU_SHARED_ACCESS_CONTROL; + + /* Check for optional write attribute. */ + if(attributes & TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE) + { + attributes_check = TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT; + } + + /* Save attribute-size register. */ + module_instance -> txm_module_instance_mpu_registers[shared_index].txm_module_mpu_region_attribute_size = attributes_check | size_register; + + /* Keep track of shared memory address and length in module instance. */ + module_instance -> txm_module_instance_shared_memory_address[module_instance -> txm_module_instance_shared_memory_count] = address; + module_instance -> txm_module_instance_shared_memory_length[module_instance -> txm_module_instance_shared_memory_count] = length; + + /* Increment counter. */ + module_instance -> txm_module_instance_shared_memory_count++; + + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return success. */ + return(TX_SUCCESS); + +#else + +ULONG block_size; +ULONG region_size; +ULONG subregion_bits; +ULONG address; +UINT attributes_check = 0; +TXM_MODULE_PREAMBLE *module_preamble; + + /* Determine if the module manager has not been initialized yet. */ + if (_txm_module_manager_ready != TX_TRUE) + { + /* Module manager has not been initialized. */ + return(TX_NOT_AVAILABLE); + } + + /* Determine if the module is valid. */ + if (module_instance == TX_NULL) + { + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Get module manager protection mutex. */ + _tx_mutex_get(&_txm_module_manager_mutex, TX_WAIT_FOREVER); + + /* Determine if the module instance is valid. */ + if (module_instance -> txm_module_instance_id != TXM_MODULE_ID) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Invalid module pointer. */ + return(TX_PTR_ERROR); + } + + /* Determine if the module instance is in the loaded state. */ + if (module_instance -> txm_module_instance_state != TXM_MODULE_LOADED) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if the module is not ready. */ + return(TX_START_ERROR); + } + + /* Check if preamble shared mem and mem protection property bits are set. */ + module_preamble = module_instance -> txm_module_instance_preamble_ptr; + if((module_preamble -> txm_module_preamble_property_flags & (TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS)) + != (TXM_MODULE_MEMORY_PROTECTION | TXM_MODULE_SHARED_EXTERNAL_MEMORY_ACCESS)) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return error if bit not set. */ + return(TXM_MODULE_INVALID_PROPERTIES); + } + + /* Start address and length must adhere to Cortex-M MPU. + The address must align with the block size. */ + + block_size = _txm_power_of_two_block_size(length); + address = (ULONG) start_address; + if(address != (address & ~(block_size - 1))) + { + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return alignment error. */ + return(TXM_MODULE_ALIGNMENT_ERROR); + } + + /* At this point, we have a valid address and block size. + Set up MPU registers. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX] = address | TXM_MODULE_MANAGER_SHARED_MPU_REGION | 0x10; + + /* Calculate the region size. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + /* Calculate the subregion bits. */ + subregion_bits = _txm_module_manager_calculate_srd_bits(block_size, length); + + /* Check for valid attributes. */ + if(attributes & TXM_MODULE_MANAGER_SHARED_ATTRIBUTE_WRITE) + { + attributes_check = TXM_MODULE_MANAGER_ATTRIBUTE_WRITE_MPU_BIT; + } + + /* Build register with attributes. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX+1] = region_size | subregion_bits | attributes_check | 0x12070001; + + /* Keep track of shared memory address and length in module instance. */ + module_instance -> txm_module_instance_shared_memory_address = address; + module_instance -> txm_module_instance_shared_memory_length = length; + + /* Recalculate MPU settings. */ + _txm_module_manager_mm_register_setup(module_instance); + + /* Release the protection mutex. */ + _tx_mutex_put(&_txm_module_manager_mutex); + + /* Return success. */ + return(TX_SUCCESS); + +#endif +} diff --git a/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_handler.c b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_handler.c new file mode 100644 index 00000000..a00a3325 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_handler.c @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/* Define the user's fault notification callback function pointer. This is + setup via the txm_module_manager_memory_fault_notify API. */ + +VOID (*_txm_module_manager_fault_notify)(TX_THREAD *, TXM_MODULE_INSTANCE *); + + +/* Define a macro that can be used to allocate global variables useful to + store information about the last fault. This macro is defined in + txm_module_port.h and is usually populated in the assembly language + fault handling prior to the code calling _txm_module_manager_memory_fault_handler. */ + +TXM_MODULE_MANAGER_FAULT_INFO + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_memory_fault_handler Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles a fault associated with a memory protected */ +/* module. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_terminate Terminate thread */ +/* */ +/* CALLED BY */ +/* */ +/* Fault handler */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_memory_fault_handler(VOID) +{ + +TXM_MODULE_INSTANCE *module_instance_ptr; +TX_THREAD *thread_ptr; + + /* Pickup the current thread. */ + thread_ptr = _tx_thread_current_ptr; + + /* Initialize the module instance pointer to NULL. */ + module_instance_ptr = TX_NULL; + + /* Is there a thread? */ + if (thread_ptr) + { + /* Pickup the module instance. */ + module_instance_ptr = thread_ptr -> tx_thread_module_instance_ptr; + + /* Terminate the current thread. */ + _tx_thread_terminate(_tx_thread_current_ptr); + } + + /* Determine if there is a user memory fault notification callback. */ + if (_txm_module_manager_fault_notify) + { + /* Yes, call the user's notification memory fault callback. */ + (_txm_module_manager_fault_notify)(thread_ptr, module_instance_ptr); + } +} diff --git a/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_notify.c b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_notify.c new file mode 100644 index 00000000..3c44ee61 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_memory_fault_notify.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "tx_thread.h" +#include "txm_module.h" + + +/* Define the external user's fault notification callback function pointer. This is + setup via the txm_module_manager_memory_fault_notify API. */ + +extern VOID (*_txm_module_manager_fault_notify)(TX_THREAD *, TXM_MODULE_INSTANCE *); + + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_memory_fault_notify Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function registers an application callback when/if a memory */ +/* fault occurs. The supplied thread is automatically terminated, but */ +/* any other threads in the same module may still execute. */ +/* */ +/* INPUT */ +/* */ +/* notify_function Memory fault notification */ +/* function, NULL disables. */ +/* */ +/* OUTPUT */ +/* */ +/* status Completion status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_memory_fault_notify(VOID (*notify_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +{ + /* Setup notification function. */ + _txm_module_manager_fault_notify = notify_function; + + /* Return success. */ + return(TX_SUCCESS); +} diff --git a/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_mm_register_setup.c b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_mm_register_setup.c new file mode 100644 index 00000000..9882e1ff --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_mm_register_setup.c @@ -0,0 +1,797 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + +#include "tx_api.h" +#include "txm_module.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_region_size_get Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function converts the region size in bytes to the block size */ +/* for the Cortex-M0+ MPU specification. */ +/* */ +/* INPUT */ +/* */ +/* block_size Size of the block in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* MPU size specification */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_mm_register_setup */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_module_manager_region_size_get(ULONG block_size) +{ + +ULONG return_value; + + /* Process relative to the input block size. */ + if (block_size == 32) + { + return_value = 0x04; + } + else if (block_size == 64) + { + return_value = 0x05; + } + else if (block_size == 128) + { + return_value = 0x06; + } + else if (block_size == 256) + { + return_value = 0x07; + } + else if (block_size == 512) + { + return_value = 0x08; + } + else if (block_size == 1024) + { + return_value = 0x09; + } + else if (block_size == 2048) + { + return_value = 0x0A; + } + else if (block_size == 4096) + { + return_value = 0x0B; + } + else if (block_size == 8192) + { + return_value = 0x0C; + } + else if (block_size == 16384) + { + return_value = 0x0D; + } + else if (block_size == 32768) + { + return_value = 0x0E; + } + else if (block_size == 65536) + { + return_value = 0x0F; + } + else if (block_size == 131072) + { + return_value = 0x10; + } + else if (block_size == 262144) + { + return_value = 0x11; + } + else if (block_size == 524288) + { + return_value = 0x12; + } + else if (block_size == 1048576) + { + return_value = 0x13; + } + else if (block_size == 2097152) + { + return_value = 0x14; + } + else + { + /* Max 4MB MPU pages for modules. */ + return_value = 0x15; + } + + return(return_value); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_calculate_srd_bits Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calculates the SRD bits that need to be set to */ +/* protect "length" bytes in a block. */ +/* */ +/* INPUT */ +/* */ +/* block_size Size of the block in bytes */ +/* length Actual length in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* SRD bits to be OR'ed with region attribute register. */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_mm_register_setup */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +ULONG _txm_module_manager_calculate_srd_bits(ULONG block_size, ULONG length) +{ + +ULONG srd_bits = 0; +UINT srd_bit_index; + + /* length is smaller than block_size, set SRD bits if block_size is 256 or more. */ + if((block_size >= 256) && (length < block_size)) + { + /* Divide block_size by 8 by shifting right 3. Result is size of subregion. */ + block_size = block_size >> 3; + + /* Set SRD index into attribute register. */ + srd_bit_index = 8; + + /* If subregion overlaps length, move to the next subregion. */ + while(length > block_size) + { + length = length - block_size; + srd_bit_index++; + } + /* Check for a portion of code remaining. */ + if(length) + { + srd_bit_index++; + } + + /* Set unused subregion bits. */ + while(srd_bit_index < 16) + { + srd_bits = srd_bits | (0x1 << srd_bit_index); + srd_bit_index++; + } + } + + return(srd_bits); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_mm_register_setup Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets up the MPU register definitions based on the */ +/* module's memory characteristics. */ +/* */ +/* Default MPU layout: */ +/* Entry Description */ +/* 0 Kernel mode entry */ +/* 1 Module code region */ +/* 2 Module code region */ +/* 3 Module code region */ +/* 4 Module code region */ +/* 5 Module data region */ +/* 6 Module data region */ +/* 7 Module data region */ +/* */ +/* If TXM_MODULE_MANAGER_16_MPU is defined, there are 16 MPU slots. */ +/* MPU layout for the Cortex-M7: */ +/* Entry Description */ +/* 0 Kernel mode entry */ +/* 1 Module code region */ +/* 2 Module code region */ +/* 3 Module code region */ +/* 4 Module code region */ +/* 5 Module data region */ +/* 6 Module data region */ +/* 7 Module data region */ +/* 8 Module data region */ +/* 9 Module shared memory region */ +/* 10 Module shared memory region */ +/* 11 Module shared memory region */ +/* 12 Unused region */ +/* 13 Unused region */ +/* 14 Unused region */ +/* 15 Unused region */ +/* */ +/* */ +/* INPUT */ +/* */ +/* module_instance Pointer to module instance */ +/* */ +/* OUTPUT */ +/* */ +/* MPU specifications for module in module_instance */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_region_size_get */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_thread_create */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance) +{ +#ifdef TXM_MODULE_MANAGER_16_MPU + +ULONG code_address; +ULONG code_size; +ULONG data_address; +ULONG data_size; +ULONG start_stop_stack_size; +ULONG callback_stack_size; +ULONG block_size; +ULONG region_size; +ULONG srd_bits = 0; +UINT mpu_table_index; +UINT i; + + + /* Setup the first MPU region for kernel mode entry. */ + /* Set address register to user mode entry function address, which is guaranteed to be at least 256-byte aligned. + Mask address to proper range, region 0, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_address = ((ULONG) _txm_module_manager_user_mode_entry & 0xFFFFFFE0) | 0x10; + /* Set the attributes, size (256 bytes) and enable bit. */ + module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MPU_KERNEL_ENTRY_INDEX].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_CODE_ACCESS_CONTROL | (_txm_module_manager_region_size_get(256) << 1) | TXM_ENABLE_REGION; + /* End of kernel mode entry setup. */ + + /* Setup code protection. */ + + /* Initialize the MPU table index. */ + mpu_table_index = 1; + + /* Pickup code starting address and actual size. */ + code_address = (ULONG) module_instance -> txm_module_instance_code_start; + code_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size; + + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MPU_CODE_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to code size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(code_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from code_size to calculate remaining space. */ + code_size = code_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(code_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + code_size = code_size - block_size; + block_size = _txm_power_of_two_block_size(code_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = (code_address & ~(block_size - 1)) | mpu_table_index | 0x10; + /* Build the attribute-size register with permissions, SRD, size, enable. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_CODE_ACCESS_CONTROL | srd_bits | region_size | TXM_ENABLE_REGION; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Increment MPU table index. */ + mpu_table_index++; + } + /* End of code protection. */ + + /* Setup data protection. */ + + /* Reset SRD bitfield. */ + srd_bits = 0; + + /* Pickup data starting address and actual size. */ + data_address = (ULONG) module_instance -> txm_module_instance_data_start; + + /* Adjust the size of the module elements to be aligned to the default alignment. We do this + so that when we partition the allocated memory, we can simply place these regions right beside + each other without having to align their pointers. Note this only works when they all have + the same alignment. */ + + data_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size; + start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size; + callback_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size; + + data_size = ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + start_stop_stack_size = ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + callback_stack_size = ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + /* Update the data size to include thread stacks. */ + data_size = data_size + start_stop_stack_size + callback_stack_size; + + /* Determine data block sizes. Minimize the alignment requirement. + There are 4 MPU data entries available. The following is how the data size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to data size. + 2. 1/4 of the largest power of two that is greater than or equal to data size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the data area. */ + for (i = 0; i < TXM_MODULE_MPU_DATA_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to data size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(data_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from data_size to calculate remaining space. */ + data_size = data_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(data_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + data_size = data_size - block_size; + block_size = _txm_power_of_two_block_size(data_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size); + } + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = (data_address & ~(block_size - 1)) | mpu_table_index | 0x10; + /* Build the attribute-size register with permissions, SRD, size, enable. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_attribute_size = TXM_MODULE_MPU_DATA_ACCESS_CONTROL | srd_bits | region_size | TXM_ENABLE_REGION; + + /* Adjust the data address. */ + data_address = data_address + block_size; + + /* Increment MPU table index. */ + mpu_table_index++; + } + + /* Setup MPU for the remaining regions. */ + while (mpu_table_index < TXM_MODULE_MPU_TOTAL_ENTRIES) + { + /* Build the base address register with address, MPU region, set Valid bit. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index].txm_module_mpu_region_address = mpu_table_index | 0x10; + + /* Increment MPU table index. */ + mpu_table_index++; + } + +#else + +ULONG code_address; +ULONG code_size; +ULONG data_address; +ULONG data_size; +ULONG start_stop_stack_size; +ULONG callback_stack_size; +ULONG block_size; +ULONG base_address_register; +ULONG base_attribute_register; +ULONG region_size; +ULONG srd_bits = 0; +UINT mpu_register = 0; +UINT mpu_table_index; +UINT i; + + + /* Setup the first region for the ThreadX trampoline code. */ + /* Set base register to user mode entry, which is guaranteed to be at least 256-byte aligned. */ + base_address_register = (ULONG) _txm_module_manager_user_mode_entry; + + /* Mask address to proper range, region 0, set Valid bit. */ + base_address_register = (base_address_register & 0xFFFFFF00) | mpu_register | 0x10; + module_instance -> txm_module_instance_mpu_registers[0] = base_address_register; + + /* Attributes: read only, write-back, shareable, size 256 bytes, region enabled. */ + module_instance -> txm_module_instance_mpu_registers[1] = 0x0607000F; + + /* Initialize the MPU register. */ + mpu_register = 1; + + /* Initialize the MPU table index. */ + mpu_table_index = 2; + + /* Setup values for code area. */ + code_address = (ULONG) module_instance -> txm_module_instance_code_start; + code_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_code_size; + + /* Check if shared memory was set up. If so, only 3 entries are available for + code protection. If not set up, 4 code entries are available. */ + if(module_instance -> txm_module_instance_mpu_registers[TXM_MODULE_MANAGER_SHARED_MPU_INDEX] == 0) + { + /* Determine code block sizes. Minimize the alignment requirement. + There are 4 MPU code entries available. The following is how the code size + will be distributed: + 1. 1/4 of the largest power of two that is greater than or equal to code size. + 2. 1/4 of the largest power of two that is greater than or equal to code size. + 3. Largest power of 2 that fits in the remaining space. + 4. Smallest power of 2 that exceeds the remaining space, minimum 256. */ + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MANAGER_CODE_MPU_ENTRIES; i++) + { + /* First two MPU blocks are 1/4 of the largest power of two + that is greater than or equal to code size. */ + if (i < 2) + { + block_size = _txm_power_of_two_block_size(code_size) >> 2; + } + + /* Third MPU block is the largest power of 2 that fits in the remaining space. */ + else if (i == 2) + { + /* Subtract (block_size*2) from code_size to calculate remaining space. */ + code_size = code_size - (block_size << 1); + block_size = _txm_power_of_two_block_size(code_size) >> 1; + } + + /* Last MPU block is the smallest power of 2 that exceeds the remaining space, minimum 256. */ + else + { + /* Calculate remaining space. */ + code_size = code_size - block_size; + block_size = _txm_power_of_two_block_size(code_size); + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Build the base address register. */ + base_address_register = (code_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x06070001; + + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + } + + /* Only 3 code entries available. */ + else + { + /* Calculate block size, one code entry taken up by shared memory. */ + block_size = _txm_power_of_two_block_size(code_size / (TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1)); + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Now loop through to setup MPU protection for the code area. */ + for (i = 0; i < TXM_MODULE_MANAGER_CODE_MPU_ENTRIES - 1; i++) + { + /* Build the base address register. */ + base_address_register = (code_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Check if SRD bits need to be set. */ + if (code_size < block_size) + { + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, code_size); + } + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x06070000; + + /* Is there still some code? If so set the region enable bit. */ + if (code_size) + { + /* Set the region enable bit. */ + base_attribute_register = base_attribute_register | 0x1; + } + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the code address. */ + code_address = code_address + block_size; + + /* Decrement the code size. */ + if (code_size > block_size) + { + code_size = code_size - block_size; + } + else + { + code_size = 0; + } + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + + /* Adjust indeces to pass over the shared memory entry. */ + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + + /* Setup values for data area. */ + data_address = (ULONG) module_instance -> txm_module_instance_data_start; + + /* Adjust the size of the module elements to be aligned to the default alignment. We do this + so that when we partition the allocated memory, we can simply place these regions right beside + each other without having to align their pointers. Note this only works when they all have + the same alignment. */ + + data_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_data_size; + start_stop_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_start_stop_stack_size; + callback_stack_size = module_instance -> txm_module_instance_preamble_ptr -> txm_module_preamble_callback_stack_size; + + data_size = ((data_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + start_stop_stack_size = ((start_stop_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + callback_stack_size = ((callback_stack_size + TXM_MODULE_DATA_ALIGNMENT - 1)/TXM_MODULE_DATA_ALIGNMENT) * TXM_MODULE_DATA_ALIGNMENT; + + /* Update the data size to include thread stacks. */ + data_size = data_size + start_stop_stack_size + callback_stack_size; + + block_size = _txm_power_of_two_block_size(data_size / TXM_MODULE_MANAGER_DATA_MPU_ENTRIES); + + /* Reset SRD bitfield. */ + srd_bits = 0; + + /* Calculate the region size information. */ + region_size = (_txm_module_manager_region_size_get(block_size) << 1); + + /* Now loop through to setup MPU protection for the data area. */ + for (i = 0; i < TXM_MODULE_MANAGER_DATA_MPU_ENTRIES; i++) + { + /* Build the base address register. */ + base_address_register = (data_address & ~(block_size - 1)) | mpu_register | 0x10; + + /* Check if SRD bits need to be set. */ + if (data_size < block_size) + { + srd_bits = _txm_module_manager_calculate_srd_bits(block_size, data_size); + } + + /* Build the base attribute register. */ + base_attribute_register = region_size | srd_bits | 0x13070000; + + /* Is there still some data? If so set the region enable bit. */ + if (data_size) + { + /* Set the region enable bit. */ + base_attribute_register = base_attribute_register | 0x1; + } + + /* Setup the MPU Base Address Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index] = base_address_register; + + /* Setup the MPU Base Attribute Register. */ + module_instance -> txm_module_instance_mpu_registers[mpu_table_index+1] = base_attribute_register; + + /* Adjust the data address. */ + data_address = data_address + block_size; + + /* Decrement the data size. */ + if (data_size > block_size) + { + data_size = data_size - block_size; + } + else + { + data_size = 0; + } + + /* Move MPU table index. */ + mpu_table_index = mpu_table_index + 2; + + /* Increment the MPU register index. */ + mpu_register++; + } + +#endif +} + +#ifdef TXM_MODULE_MANAGER_16_MPU +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_inside_data_check Cortex-M0+ */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks if the specified object is inside shared */ +/* memory. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Pointer to module instance */ +/* obj_ptr Pointer to the object */ +/* obj_size Size of the object */ +/* */ +/* OUTPUT */ +/* */ +/* Whether the object is inside the shared memory region. */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Module dispatch check functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, UINT obj_size) +{ + +UINT shared_memory_index; +UINT num_shared_memory_mpu_entries; +ALIGN_TYPE shared_memory_address_start; +ALIGN_TYPE shared_memory_address_end; + + /* Check for overflow. */ + if ((obj_ptr) > ((obj_ptr) + (obj_size))) + { + return(TX_FALSE); + } + + /* Check if the object is inside the module data. */ + if ((obj_ptr >= (ALIGN_TYPE) module_instance -> txm_module_instance_data_start) && + ((obj_ptr + obj_size) <= ((ALIGN_TYPE) module_instance -> txm_module_instance_data_end + 1))) + { + return(TX_TRUE); + } + + /* Check if the object is inside the shared memory. */ + num_shared_memory_mpu_entries = module_instance -> txm_module_instance_shared_memory_count; + for (shared_memory_index = 0; shared_memory_index < num_shared_memory_mpu_entries; shared_memory_index++) + { + + shared_memory_address_start = (ALIGN_TYPE) module_instance -> txm_module_instance_shared_memory_address[shared_memory_index]; + shared_memory_address_end = shared_memory_address_start + module_instance -> txm_module_instance_shared_memory_length[shared_memory_index]; + + if ((obj_ptr >= (ALIGN_TYPE) shared_memory_address_start) && + ((obj_ptr + obj_size) <= (ALIGN_TYPE) shared_memory_address_end)) + { + return(TX_TRUE); + } + } + + return(TX_FALSE); +} +#endif diff --git a/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_thread_stack_build.S b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_thread_stack_build.S new file mode 100644 index 00000000..9fe766d4 --- /dev/null +++ b/ports_module/cortex_m0+/iar/module_manager/src/txm_module_manager_thread_stack_build.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Module Manager */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + SECTION `.text`:CODE:NOROOT(2) + THUMB +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_thread_stack_build Cortex-M0+/IAR */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to shell function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _txm_module_manager_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +// { + PUBLIC _txm_module_manager_thread_stack_build +_txm_module_manager_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + MOVS r3, #0x7 // + BICS r2, r2, r3 // Align frame for 8-byte alignment + SUBS r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOVS r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r8 + STR r3, [r2, #12] // Store initial r10 + STR r3, [r2, #16] // Store initial r11 + STR r3, [r2, #20] // Store initial r4 + STR r3, [r2, #24] // Store initial r5 + STR r3, [r2, #28] // Store initial r6 + STR r3, [r2, #32] // Store initial r7 + + /* Hardware stack follows. */ + + STR r0, [r2, #36] // Store initial r0, which is the thread control block + + LDR r3, [r0, #8] // Pickup thread entry info pointer,which is in the stack pointer position of the thread control block. + // It was setup in the txm_module_manager_thread_create function. It will be overwritten later in this + // function with the actual, initial stack pointer. + STR r3, [r2, #40] // Store initial r1, which is the module entry information. + LDR r3, [r3, #8] // Pickup data base register from the module information + STR r3, [r2, #8] // Store initial r9 (data base register) + MOVS r3, #0 // Clear r3 again + + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + LDR r3, =0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + LDR r3, =0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's control block + BX lr // Return to caller +// } + END diff --git a/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvoptx b/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvoptx index 2614df9e..2964aed9 100644 --- a/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvoptx +++ b/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvoptx @@ -2386,30 +2386,6 @@ 1 185 - 1 - 0 - 0 - 0 - ..\module_manager\src\tx_thread_stack_error_handler.c - tx_thread_stack_error_handler.c - 0 - 0 - - - 1 - 186 - 1 - 0 - 0 - 0 - ..\module_manager\src\tx_thread_stack_error_notify.c - tx_thread_stack_error_notify.c - 0 - 0 - - - 1 - 187 2 0 0 @@ -2959,6 +2935,30 @@ 0 0 + + 1 + 231 + 1 + 0 + 0 + 0 + ..\..\..\..\common\src\tx_thread_stack_error_handler.c + tx_thread_stack_error_handler.c + 0 + 0 + + + 1 + 232 + 1 + 0 + 0 + 0 + ..\..\..\..\common\src\tx_thread_stack_error_notify.c + tx_thread_stack_error_notify.c + 0 + 0 + diff --git a/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvprojx b/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvprojx index cdf3f904..5a6b07b9 100644 --- a/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvprojx +++ b/ports_module/cortex_m23/ac6/example_build/ThreadX_Library.uvprojx @@ -1303,16 +1303,6 @@ 1 ..\module_manager\src\txe_thread_secure_stack_free.c - - tx_thread_stack_error_handler.c - 1 - ..\module_manager\src\tx_thread_stack_error_handler.c - - - tx_thread_stack_error_notify.c - 1 - ..\module_manager\src\tx_thread_stack_error_notify.c - tx_thread_context_restore.S 2 @@ -1543,6 +1533,16 @@ 2 ..\module_manager\src\tx_thread_secure_stack_initialize.S + + tx_thread_stack_error_handler.c + 1 + ..\..\..\..\common\src\tx_thread_stack_error_handler.c + + + tx_thread_stack_error_notify.c + 1 + ..\..\..\..\common\src\tx_thread_stack_error_notify.c + diff --git a/ports_module/cortex_m23/ac6/inc/tx_secure_interface.h b/ports_module/cortex_m23/ac6/inc/tx_secure_interface.h index 0a2bdd67..d6fba106 100644 --- a/ports_module/cortex_m23/ac6/inc/tx_secure_interface.h +++ b/ports_module/cortex_m23/ac6/inc/tx_secure_interface.h @@ -51,9 +51,10 @@ /* Define internal secure thread stack function prototypes. */ -extern void _tx_thread_secure_stack_initialize(void); +extern UINT _tx_thread_secure_mode_stack_initialize(void); extern UINT _tx_thread_secure_mode_stack_allocate(TX_THREAD *thread_ptr, ULONG stack_size); extern UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr); +extern void _tx_thread_secure_stack_initialize(void); extern void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr); extern void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr); diff --git a/ports_module/cortex_m23/ac6/inc/txm_module_port.h b/ports_module/cortex_m23/ac6/inc/txm_module_port.h index cdc1b1bd..ec009327 100644 --- a/ports_module/cortex_m23/ac6/inc/txm_module_port.h +++ b/ports_module/cortex_m23/ac6/inc/txm_module_port.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* txm_module_port.h Cortex-M23/AC6 */ -/* 6.1.6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 04-02-2021 Scott Larson Initial Version 6.1.6 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user-configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -90,6 +93,11 @@ The following extensions must also be defined in tx_port.h: VOID (*tx_timer_module_expiration_function)(ULONG id); */ +/* Users can define the module heap size. */ +#ifndef TXM_MODULE_HEAP_SIZE +#define TXM_MODULE_HEAP_SIZE 512 +#endif + /* Define the kernel stack size for a module thread. */ #ifndef TXM_MODULE_KERNEL_STACK_SIZE #define TXM_MODULE_KERNEL_STACK_SIZE 768 @@ -106,6 +114,8 @@ The following extensions must also be defined in tx_port.h: /* Define the properties for this particular module port. */ +#define TXM_MODULE_PORT_DISPATCH + #define TXM_MODULE_MEMORY_PROTECTION_ENABLED #ifdef TXM_MODULE_MEMORY_PROTECTION_ENABLED @@ -347,6 +357,6 @@ ALIGN_TYPE _txm_module_manager_port_dispatch(TXM_MODULE_INSTANCE *module_instanc #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M23/AC6 Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M23/AC6 Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m23/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_m23/ac6/module_lib/src/txm_module_initialize.S index 2221f2fd..965a7cd9 100644 --- a/ports_module/cortex_m23/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_m23/ac6/module_lib/src/txm_module_initialize.S @@ -33,7 +33,7 @@ /* FUNCTION RELEASE */ /* */ /* _txm_module_initialize Cortex-M23/AC6 */ -/* 6.1.6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -63,12 +63,17 @@ /* DATE NAME DESCRIPTION */ /* */ /* 04-02-2021 Scott Larson Initial Version 6.1.6 */ +/* 01-31-2022 Scott Larson Modified comments, fixed */ +/* scatterload, and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _txm_module_initialize(VOID) .global _txm_module_initialize .thumb_func _txm_module_initialize: + PUSH {r0-r3} // Save r0-r3 MOV r0, r8 // Copy r8-r11 MOV r1, r9 MOV r2, r10 @@ -78,7 +83,7 @@ _txm_module_initialize: MOV r1, lr PUSH {r0-r1} // Save r12, lr - //B __scatterload // Call ARM func to initialize variables + B __scatterload // Call ARM func to initialize variables // Override the __rt_exit function. .global __rt_exit @@ -94,25 +99,21 @@ __rt_exit: MOV r11, r3 BX lr // Return to caller -#define TXM_MODULE_HEAP_SIZE 512 - -// returns heap start address in R0 -// returns heap end address in R2 -// does not touch SP, it is already set up before the module runs - .global __user_setup_stackheap - .thumb_func -__user_setup_stackheap: - LDR r1, _txm_heap // load heap offset - MOV r2, TXM_MODULE_HEAP_SIZE // load heap size - ADD r2, r2, r0 // calculate heap end address - BX lr - -// dummy main function - .global main - .thumb_func -main: - BX lr - - .align 8 -_txm_heap: - .zero TXM_MODULE_HEAP_SIZE +// Override the __rt_entry function. + .global __rt_entry + .type __rt_entry, %function +__rt_entry: + MOV r0,sp + ADDS r0,r0,#40 + LDM r0,{r0-r1} + BL __rt_lib_init // Call ARM func to initialize library + POP {r0-r1} // Restore dregs and LR + MOV r12, r0 + MOV lr, r1 + POP {r0-r7} + MOV r8, r0 + MOV r9, r1 + MOV r10, r2 + MOV r11, r3 + POP {r0-r3} + BX lr // Return to caller diff --git a/ports_module/cortex_m23/ac6/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m23/ac6/module_lib/src/txm_module_thread_shell_entry.c index 9a952615..ca2a267d 100644 --- a/ports_module/cortex_m23/ac6/module_lib/src/txm_module_thread_shell_entry.c +++ b/ports_module/cortex_m23/ac6/module_lib/src/txm_module_thread_shell_entry.c @@ -28,12 +28,13 @@ #define TX_SOURCE_CODE #endif - /* Include necessary system files. */ #include "txm_module.h" #include "tx_thread.h" +__asm(".global __ARM_use_no_argv"); + /* Define the global module entry pointer from the start thread of the module. */ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; @@ -44,23 +45,27 @@ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param3); -/* Define the ARM cstartup code. */ -extern VOID _txm_module_initialize(VOID); +/* Define the module's heap and align it to 8 bytes. */ +__attribute__((aligned(8))) UCHAR txm_heap[TXM_MODULE_HEAP_SIZE]; + + +/* Use our asm routine that calls the ARM code to initialize data and heap. */ +extern VOID _txm_module_initialize(VOID *heap_base, VOID *heap_top); /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _txm_module_thread_shell_entry Cortex-M23/AC6 */ -/* 6.1.6 */ +/* _txm_module_thread_shell_entry Cortex-M23/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function calls the specified entry function of the thread. It */ +/* This function calls the specified entry function of the thread. It */ /* also provides a place for the thread's entry function to return. */ /* If the thread returns, this function places the thread in a */ /* "COMPLETED" state. */ @@ -90,6 +95,10 @@ extern VOID _txm_module_initialize(VOID); /* DATE NAME DESCRIPTION */ /* */ /* 04-02-2021 Scott Larson Initial Version 6.1.6 */ +/* 01-31-2022 Scott Larson Modified comments, fixed */ +/* scatterload, and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) @@ -99,13 +108,12 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN VOID (*entry_exit_notify)(TX_THREAD *, UINT); #endif - /* Determine if this is the start thread. If so, we must prepare the module for execution. If not, simply skip the C startup code. */ if (thread_info -> txm_module_thread_entry_info_start_thread) { - /* Initialize the ARM C environment. */ - _txm_module_initialize(); + /* Initialize the C environment. */ + _txm_module_initialize(&txm_heap[0], &txm_heap[TXM_MODULE_HEAP_SIZE-1]); /* Save the entry info pointer, for later use. */ _txm_module_entry_info = thread_info; @@ -169,4 +177,3 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); #endif } - diff --git a/ports_module/cortex_m3/ac5/example_build/build.bat b/ports_module/cortex_m3/ac5/example_build/build.bat deleted file mode 100644 index 57eae574..00000000 --- a/ports_module/cortex_m3/ac5/example_build/build.bat +++ /dev/null @@ -1,8 +0,0 @@ -@ECHO OFF -ECHO Starting build... -CALL build_threadx.bat -CALL build_threadx_demo.bat -CALL build_threadx_module_library.bat -CALL build_threadx_module_demo.bat -CALL build_threadx_module_manager_demo.bat -ECHO Build finished. diff --git a/ports_module/cortex_m3/ac5/example_build/clean.bat b/ports_module/cortex_m3/ac5/example_build/clean.bat deleted file mode 100644 index 3217f01b..00000000 --- a/ports_module/cortex_m3/ac5/example_build/clean.bat +++ /dev/null @@ -1,2 +0,0 @@ -@ECHO OFF -DEL *.o *.a *.axf *.map diff --git a/ports_module/cortex_m3/ac5/example_build/setenv.bat b/ports_module/cortex_m3/ac5/example_build/setenv.bat deleted file mode 100644 index 965f12f3..00000000 --- a/ports_module/cortex_m3/ac5/example_build/setenv.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off - -REM *** ARM DS 2020 -REM SET PATH=%ProgramFiles%\Arm\Development Studio 2020.0\sw\ARMCompiler5.06u6\bin;%PATH% -REM SET ARMLMD_LICENSE_FILE=%APPDATA%\arm\ds\licenses -REM SET ARM_CONFIG_PATH=%APPDATA%\arm\ds\2020.0 -REM SET ARM_PRODUCT_DEF=%ProgramFiles%\Arm\Development Studio 2020.0\sw\mappings\gold.elmap - -REM *** legacy ARM DS 5 -SET PATH=%ProgramFiles%\DS-5 v5.29.3\sw\ARMCompiler5.06u6\bin;%PATH% -SET ARMLMD_LICENSE_FILE=%APPDATA%\ARM\DS-5\licenses -SET ARM_CONFIG_PATH=%APPDATA%\ARM\DS-5_v5.29.3 -SET ARM_PRODUCT_PATH=%ProgramFiles%\DS-5 v5.29.3\sw\mappings diff --git a/ports_module/cortex_m3/ac5/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_m3/ac5/module_manager/src/tx_timer_interrupt.s index 60717ba3..bfab75e6 100644 --- a/ports_module/cortex_m3/ac5/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/cortex_m3/ac5/module_manager/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M3/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER EXPORT _tx_timer_interrupt _tx_timer_interrupt @@ -249,6 +253,7 @@ __tx_timer_nothing_expired DSB // Complete all memory access BX lr // Return to caller // } +#endif ALIGN LTORG END diff --git a/ports_module/cortex_m3/ac6/example_build/all.bat b/ports_module/cortex_m3/ac6/example_build/all.bat deleted file mode 100644 index 748e5743..00000000 --- a/ports_module/cortex_m3/ac6/example_build/all.bat +++ /dev/null @@ -1,5 +0,0 @@ -@ECHO OFF -CALL clean.bat -CALL setenv.bat -CALL initws.bat -CALL build.bat diff --git a/ports_module/cortex_m3/ac6/example_build/build.bat b/ports_module/cortex_m3/ac6/example_build/build.bat deleted file mode 100644 index 6ebdf460..00000000 --- a/ports_module/cortex_m3/ac6/example_build/build.bat +++ /dev/null @@ -1,24 +0,0 @@ -@ECHO OFF - -ECHO Build starting... - -SETLOCAL ENABLEEXTENSIONS - -IF DEFINED ARMDSIDEC GOTO IARBUILD_DEFINED -ECHO ERROR: please set ARMDSIDEC to the path of the ARM Developer Studio eclipsec.exe program -EXIT /B 2 -:IARBUILD_DEFINED - -IF EXIST %ARMDSIDEC% GOTO ARMDSIDEC_FOUND -ECHO ERROR: the command ARMDSIDEC doesn't exist: %ARMDSIDEC% -EXIT /B 2 -:ARMDSIDEC_FOUND - -%ARMDSIDEC% -nosplash -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data .\workspace -build all -IF %ERRORLEVEL% EQU 0 GOTO BUILD_OK -ECHO ERROR: build failed. -EXIT /B 1 -:BUILD_OK - -ECHO Build completed without errors. -EXIT /B 0 diff --git a/ports_module/cortex_m3/ac6/example_build/clean.bat b/ports_module/cortex_m3/ac6/example_build/clean.bat deleted file mode 100644 index 8a48e5c9..00000000 --- a/ports_module/cortex_m3/ac6/example_build/clean.bat +++ /dev/null @@ -1,4 +0,0 @@ -@ECHO OFF -ECHO Cleaning... -RMDIR /Q /S workspace -ECHO Done. diff --git a/ports_module/cortex_m3/ac6/example_build/initws.bat b/ports_module/cortex_m3/ac6/example_build/initws.bat deleted file mode 100644 index 62806594..00000000 --- a/ports_module/cortex_m3/ac6/example_build/initws.bat +++ /dev/null @@ -1,14 +0,0 @@ -@ECHO OFF - -ECHO Initializing the workspace... - -SETLOCAL ENABLEEXTENSIONS - -%ARMDSIDEC% -nosplash -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data .\workspace -import .\tx -import .\txm -import .\sample_threadx -import .\sample_threadx_module -import .\sample_threadx_module_manager -IF %ERRORLEVEL% EQU 0 GOTO WS_INITIALIZED -ECHO ERROR: failed to initialize the workspace -EXIT /B 2 - -:WS_INITIALIZED -echo Workspace initialized. -EXIT /B 0 diff --git a/ports_module/cortex_m3/ac6/example_build/setenv.bat b/ports_module/cortex_m3/ac6/example_build/setenv.bat deleted file mode 100644 index 27fecdfc..00000000 --- a/ports_module/cortex_m3/ac6/example_build/setenv.bat +++ /dev/null @@ -1,16 +0,0 @@ -@ECHO OFF - -SET ARMDSDIR="C:\Program Files\Arm\Development Studio 2020.0" -IF EXIST %ARMDSDIR% GOTO FOUND_ARMDS -ECHO ARM Development Studio not found. -EXIT /B 1 - -:FOUND_ARMDS -SET ARMDSIDEC=%ARMDSDIR%\bin\armds_idec.exe -IF EXIST %ARMDSIDEC% GOTO FOUND_ARMDS_IDEC -ECHO armds_idec.exe not found. -EXIT /B 1 - -:FOUND_ARMDS_IDEC -ECHO armds_idec.exe found at %ARMDSIDEC% -EXIT /B 0 diff --git a/ports_module/cortex_m3/ac6/inc/txm_module_port.h b/ports_module/cortex_m3/ac6/inc/txm_module_port.h index 8dba095c..75621757 100644 --- a/ports_module/cortex_m3/ac6/inc/txm_module_port.h +++ b/ports_module/cortex_m3/ac6/inc/txm_module_port.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* txm_module_port.h Cortex-M3/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user-configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -91,6 +94,11 @@ The following extensions must also be defined in tx_port.h: VOID (*tx_timer_module_expiration_function)(ULONG id); */ +/* Users can define the module heap size. */ +#ifndef TXM_MODULE_HEAP_SIZE +#define TXM_MODULE_HEAP_SIZE 512 +#endif + /* Define the kernel stack size for a module thread. */ #ifndef TXM_MODULE_KERNEL_STACK_SIZE #define TXM_MODULE_KERNEL_STACK_SIZE 768 diff --git a/ports_module/cortex_m3/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_m3/ac6/module_lib/src/txm_module_initialize.S index 488002a4..8664208f 100644 --- a/ports_module/cortex_m3/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_m3/ac6/module_lib/src/txm_module_initialize.S @@ -33,7 +33,7 @@ /* FUNCTION RELEASE */ /* */ /* _txm_module_initialize Cortex-M3/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -63,14 +63,17 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _txm_module_initialize(VOID) .global _txm_module_initialize .thumb_func _txm_module_initialize: - PUSH {r4-r12,lr} // Save dregs and LR - //B __scatterload // Call ARM func to initialize variables + PUSH {r0-r12,lr} // Save dregs and LR + B __scatterload // Call ARM func to initialize variables // Override the __rt_exit function. .global __rt_exit @@ -79,25 +82,10 @@ __rt_exit: POP {r4-r12,lr} // Restore dregs and LR BX lr // Return to caller -#define TXM_MODULE_HEAP_SIZE 512 - -// returns heap start address in R0 -// returns heap end address in R2 -// does not touch SP, it is already set up before the module runs - .global __user_setup_stackheap - .thumb_func -__user_setup_stackheap: - LDR r1, _txm_heap // load heap offset - MOV r2, TXM_MODULE_HEAP_SIZE // load heap size - ADD r2, r2, r0 // calculate heap end address - BX lr - -// dummy main function - .global main - .thumb_func -main: - BX lr - - .align 8 -_txm_heap: - .zero TXM_MODULE_HEAP_SIZE + .global __rt_entry + .type __rt_entry, %function +__rt_entry: + POP {r0-r1} + BL __rt_lib_init + POP {r2-r12,lr} // Restore dregs and LR + BX lr // Return to caller diff --git a/ports_module/cortex_m3/ac6/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m3/ac6/module_lib/src/txm_module_thread_shell_entry.c index 20bb514d..efe4d9b0 100644 --- a/ports_module/cortex_m3/ac6/module_lib/src/txm_module_thread_shell_entry.c +++ b/ports_module/cortex_m3/ac6/module_lib/src/txm_module_thread_shell_entry.c @@ -44,10 +44,12 @@ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param3); -/* Define the startup code that clears the uninitialized global data and sets up the - preset global variables. */ +/* Define the module's heap and align it to 8 bytes. */ +__attribute__((aligned(8))) UCHAR txm_heap[TXM_MODULE_HEAP_SIZE]; -extern VOID _txm_module_initialize(VOID); + +/* Use our asm routine that calls the ARM code to initialize data and heap. */ +extern VOID _txm_module_initialize(VOID *heap_base, VOID *heap_top); /**************************************************************************/ @@ -55,7 +57,7 @@ extern VOID _txm_module_initialize(VOID); /* FUNCTION RELEASE */ /* */ /* _txm_module_thread_shell_entry Cortex-M3/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -92,6 +94,9 @@ extern VOID _txm_module_initialize(VOID); /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) @@ -107,7 +112,7 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN if (thread_info -> txm_module_thread_entry_info_start_thread) { /* Initialize the C environment. */ - _txm_module_initialize(); + _txm_module_initialize(&txm_heap[0], &txm_heap[TXM_MODULE_HEAP_SIZE-1]); /* Save the entry info pointer, for later use. */ _txm_module_entry_info = thread_info; diff --git a/ports_module/cortex_m3/ac6/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m3/ac6/module_manager/src/tx_timer_interrupt.S index 752e337f..1e61de61 100644 --- a/ports_module/cortex_m3/ac6/module_manager/src/tx_timer_interrupt.S +++ b/ports_module/cortex_m3/ac6/module_manager/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M3/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports_module/cortex_m3/gnu/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m3/gnu/module_manager/src/tx_thread_schedule.S index 02ef7522..b19b6cc8 100644 --- a/ports_module/cortex_m3/gnu/module_manager/src/tx_thread_schedule.S +++ b/ports_module/cortex_m3/gnu/module_manager/src/tx_thread_schedule.S @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M3/GNU */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -74,6 +74,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -92,7 +94,7 @@ _tx_thread_schedule: LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag STR r0, [r2, #0] // Clear preempt disable flag -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit @@ -191,7 +193,7 @@ UsageFault_Handler: // Bit 7 = 1 -> MMFAR is valid STRB r1, [r0] // Clear the MMFSR -#ifdef __ARM_PCS_VFP +#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 @@ -258,7 +260,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -383,7 +385,7 @@ __tx_ts_restore: STR r1, [r0] // Enable MPU skip_mpu_setup: LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers @@ -448,14 +450,14 @@ __tx_SVCallHandler: #endif MRS r3, PSP // Pickup thread stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST lr, #0x10 // Test for extended module stack ITT EQ ORREQ r3, r3, #1 // If so, set LSB in thread stack pointer to indicate extended frame ORREQ lr, lr, #0x10 // Set bit, return with standard frame #endif STR r3, [r2, #0xB0] // Save thread stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP BIC r3, #1 // Clear possibly OR'd bit #endif @@ -504,7 +506,7 @@ _tx_thread_user_return: STR r3, [r2, #20] // Set stack size #endif -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* If lazy stacking is pending, check if it can be cleared. if(LSPACT && tx_thread_module_stack_start < FPCAR && FPCAR < tx_thread_module_stack_end) then clear LSPACT. */ @@ -528,7 +530,7 @@ _tx_no_lazy_clear: LDR r0, [r2, #0xB0] // Load the module thread stack pointer MRS r3, PSP // Pickup kernel stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST r0, #1 // Is module stack extended? ITTE NE // If so... BICNE lr, #0x10 // Clear bit, return with extended frame @@ -589,7 +591,7 @@ _txm_module_user_mode_exit: NOP // } -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP .global tx_thread_fpu_enable .thumb_func diff --git a/ports_module/cortex_m3/gnu/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m3/gnu/module_manager/src/tx_timer_interrupt.S index 2edd6db1..5c3d6a2e 100644 --- a/ports_module/cortex_m3/gnu/module_manager/src/tx_timer_interrupt.S +++ b/ports_module/cortex_m3/gnu/module_manager/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M3/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports_module/cortex_m3/iar/inc/tx_port.h b/ports_module/cortex_m3/iar/inc/tx_port.h index 9353ac8e..c9c13b3c 100644 --- a/ports_module/cortex_m3/iar/inc/tx_port.h +++ b/ports_module/cortex_m3/iar/inc/tx_port.h @@ -509,7 +509,7 @@ void tx_thread_fpu_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M3/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M3/IAR Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports_module/cortex_m3/iar/inc/txm_module_port.h b/ports_module/cortex_m3/iar/inc/txm_module_port.h index 8e69b96b..d3f38fec 100644 --- a/ports_module/cortex_m3/iar/inc/txm_module_port.h +++ b/ports_module/cortex_m3/iar/inc/txm_module_port.h @@ -377,6 +377,6 @@ UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M3/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M3/IAR Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m3/iar/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_m3/iar/module_manager/src/tx_timer_interrupt.s index 13427464..29ee9a3a 100644 --- a/ports_module/cortex_m3/iar/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/cortex_m3/iar/module_manager/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M3/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER PUBLIC _tx_timer_interrupt _tx_timer_interrupt: @@ -249,4 +253,5 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif END diff --git a/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvoptx b/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvoptx index 25c1033c..015946e6 100644 --- a/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvoptx +++ b/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvoptx @@ -2386,30 +2386,6 @@ 1 185 - 1 - 0 - 0 - 0 - ..\module_manager\src\tx_thread_stack_error_handler.c - tx_thread_stack_error_handler.c - 0 - 0 - - - 1 - 186 - 1 - 0 - 0 - 0 - ..\module_manager\src\tx_thread_stack_error_notify.c - tx_thread_stack_error_notify.c - 0 - 0 - - - 1 - 187 2 0 0 @@ -2959,6 +2935,30 @@ 0 0 + + 1 + 231 + 1 + 0 + 0 + 0 + ..\..\..\..\common\src\tx_thread_stack_error_handler.c + tx_thread_stack_error_handler.c + 0 + 0 + + + 1 + 232 + 1 + 0 + 0 + 0 + ..\..\..\..\common\src\tx_thread_stack_error_notify.c + tx_thread_stack_error_notify.c + 0 + 0 + diff --git a/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvprojx b/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvprojx index a9b1b0d4..f15b4b9f 100644 --- a/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvprojx +++ b/ports_module/cortex_m33/ac6/example_build/ThreadX_Library.uvprojx @@ -1303,16 +1303,6 @@ 1 ..\module_manager\src\txe_thread_secure_stack_free.c - - tx_thread_stack_error_handler.c - 1 - ..\module_manager\src\tx_thread_stack_error_handler.c - - - tx_thread_stack_error_notify.c - 1 - ..\module_manager\src\tx_thread_stack_error_notify.c - tx_thread_context_restore.S 2 @@ -1543,6 +1533,16 @@ 2 ..\module_manager\src\tx_thread_secure_stack_initialize.S + + tx_thread_stack_error_handler.c + 1 + ..\..\..\..\common\src\tx_thread_stack_error_handler.c + + + tx_thread_stack_error_notify.c + 1 + ..\..\..\..\common\src\tx_thread_stack_error_notify.c + diff --git a/ports_module/cortex_m33/ac6/inc/tx_port.h b/ports_module/cortex_m33/ac6/inc/tx_port.h index d16017af..eb4a7f5f 100644 --- a/ports_module/cortex_m33/ac6/inc/tx_port.h +++ b/ports_module/cortex_m33/ac6/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h Cortex-M33/AC6 */ -/* 6.1.9 */ +/* tx_port.h Cortex-M33 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -43,21 +43,31 @@ /* own special types that can be mapped to actual data types by this */ /* file to guarantee consistency in the interface and functionality. */ /* */ +/* This file replaces the previous Cortex-M33 files. It unifies */ +/* the Cortex-M33 compilers into one common file. */ +/* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 Scott Larson Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ -/* macro definition, */ -/* resulting in version 6.1.6 */ -/* 06-02-2021 Bhupendra Naphade Modified comment(s), */ +/* 03-02-2021 Scott Larson Modified comment(s), added */ +/* ULONG64_DEFINED, */ +/* resulting in version 6.1.5 */ +/* 06-02-2021 Scott Larson Modified comment(s), removed */ +/* unneeded header file, funcs */ +/* set_control and get_control */ +/* changed to inline, */ /* added symbol to enable */ /* stack error handler, */ /* resulting in version 6.1.7 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* symbol ULONG64_DEFINED, */ +/* 10-15-2021 Scott Larson Modified comment(s), improved */ +/* stack check error handling, */ /* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), unified */ +/* this file across compilers, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -67,17 +77,32 @@ /* Determine if the optional ThreadX user define file should be used. */ #ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may +/* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" -#endif +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ /* Define compiler library include files. */ #include #include + +#ifdef __ICCARM__ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm from each compiler look similar */ +#define _tx_control_get __get_CONTROL +#define _tx_control_set __set_CONTROL +#define _tx_ipsr_get __get_IPSR +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ +#endif /* __ICCARM__ */ + +#ifdef __ARMCOMPILER_VERSION #include +#endif + /* Define ThreadX basic types for this port. */ @@ -100,35 +125,17 @@ UINT _txe_thread_secure_stack_free(struct TX_THREAD_STRUCT *thread_ptr); UINT _tx_thread_secure_stack_allocate(struct TX_THREAD_STRUCT *tx_thread, ULONG stack_size); UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); -/* This port overrides tx_thread_stack_error_notify with an architecture specific version */ -#define TX_PORT_THREAD_STACK_ERROR_NOTIFY - -/* This port overrides tx_thread_stack_error_handler with an architecture specific version */ -#define TX_PORT_THREAD_STACK_ERROR_HANDLER - -/* This hardware has stack checking that we take advantage of - do NOT define. */ -#ifdef TX_ENABLE_STACK_CHECKING - #error "Do not define TX_ENABLE_STACK_CHECKING" -#endif - -/* If user does not want to terminate thread on stack overflow, - #define the TX_THREAD_NO_TERMINATE_STACK_ERROR symbol. - The thread will be rescheduled and continue to cause the exception. - It is suggested user code handle this by registering a notification with the - tx_thread_stack_error_notify function. */ -/*#define TX_THREAD_NO_TERMINATE_STACK_ERROR */ - -/* Define the system API mappings based on the error checking - selected by the user. Note: this section is only applicable to +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to application source code, hence the conditional that turns off this stuff when the include file is processed by the ThreadX source. */ #ifndef TX_SOURCE_CODE -/* Determine if error checking is desired. If so, map API functions +/* Determine if error checking is desired. If so, map API functions to the appropriate error checking front-ends. Otherwise, map API - functions to the core functions that actually perform the work. + functions to the core functions that actually perform the work. Note: error checking is enabled by default. */ #ifdef TX_DISABLE_ERROR_CHECKING @@ -145,10 +152,11 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define tx_thread_secure_stack_allocate _txe_thread_secure_stack_allocate #define tx_thread_secure_stack_free _txe_thread_secure_stack_free -#endif -#endif - +#endif /* TX_DISABLE_ERROR_CHECKING */ +#endif /* TX_SOURCE_CODE */ +/* This port has a usage fault handler in _tx_initialize_low_level for stack exceptions. */ +#define TX_PORT_THREAD_STACK_ERROR_HANDLING /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ @@ -173,19 +181,19 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ #endif -#ifndef TX_TIMER_THREAD_PRIORITY +#ifndef TX_TIMER_THREAD_PRIORITY #define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ #endif -/* Define various constants for the ThreadX Cortex-M33 port. */ +/* Define various constants for the ThreadX Cortex-M port. */ #define TX_INT_DISABLE 1 /* Disable interrupts */ #define TX_INT_ENABLE 0 /* Enable interrupts */ /* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock source constants would be: #define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) @@ -224,7 +232,7 @@ ULONG _tx_misra_time_stamp_get(VOID); #endif -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING define is negated, thereby forcing the stack fill which is necessary for the stack checking @@ -238,41 +246,86 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with + for the multiple macros is so that backward compatibility can be maintained with existing ThreadX kernel awareness modules. */ #define TX_THREAD_EXTENSION_0 #define TX_THREAD_EXTENSION_1 + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +/* IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_secure_stack_context; +/* ThreadX in non-secure zone with calls to secure zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_secure_stack_context; \ + VOID *tx_thread_iar_tls_pointer; #else -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; +/* ThreadX in only one zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_iar_tls_pointer; #endif + +#else +/* No IAR library support */ +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) +/* ThreadX in non-secure zone with calls to secure zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_secure_stack_context; +#else +/* ThreadX in only one zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; +#endif + +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + #define TX_THREAD_EXTENSION_3 @@ -294,7 +347,7 @@ ULONG _tx_misra_time_stamp_get(VOID); VOID (*tx_timer_module_expiration_function)(ULONG id); -/* Define the user extension field of the thread control block. Nothing +/* Define the user extension field of the thread control block. Nothing additional is needed for this port so it is defined as white space. */ #ifndef TX_THREAD_USER_EXTENSION @@ -304,38 +357,39 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, tx_thread_shell_entry, and tx_thread_terminate. */ - - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ + if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} +#else +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#endif +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); + +#else /* No IAR library support. */ +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) #endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* Define the size of the secure stack for the timer thread and use the extension to allocate the secure stack. */ -#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 +#define TX_TIMER_THREAD_SECURE_STACK_SIZE 256 #define TX_TIMER_INITIALIZE_EXTENSION(status) _tx_thread_secure_stack_allocate(&_tx_timer_thread, TX_TIMER_THREAD_SECURE_STACK_SIZE); #endif -#ifndef TX_MISRA_ENABLE - -//register unsigned int _ipsr __asm ("MRS %[result], ipsr" : [result] "=r" (_ipsr) : ); -inline static unsigned int _get_ipsr(void); -inline static unsigned int _get_ipsr(void) -{ - unsigned int _ipsr; - __asm("MRS %[result], ipsr" : [result] "=r" (_ipsr) : ); - return _ipsr; -} - -#endif - - -#ifdef __ARM_PCS_VFP +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -344,128 +398,128 @@ void _tx_misra_control_set(ULONG value); ULONG _tx_misra_fpccr_get(void); void _tx_misra_vfp_touch(void); -#else +#else /* TX_MISRA_ENABLE not defined */ -#ifdef TX_SOURCE_CODE +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ -static unsigned int _get_control(void); -static unsigned int _get_control(void) +__attribute__( ( always_inline ) ) static inline ULONG _tx_control_get(void) { - unsigned int _control; - __asm("MRS %[result], control" : [result] "=r" (_control) : ); - return _control; +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); } -static void _set_control(unsigned int _control); -static void _set_control(unsigned int _control) +__attribute__( ( always_inline ) ) static inline void _tx_control_set(ULONG control_value) { - __asm("MSR control, %[input]" : : [input] "r" (_control)); + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); } -#endif -#endif +#endif /* __GNUC__ */ + +/* Touch VFP register in order to flush. Works for AC6/GCC/IAR compilers. */ +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); + +#endif /* TX_MISRA_ENABLE */ /* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA in order to ensure no lazy stacking will occur. */ #ifndef TX_MISRA_ENABLE -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state);; \ - } +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } #else -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } - +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } #endif /* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating - this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush the lazy FPU save, then restore the CONTROL.FPCA state. */ #ifndef TX_MISRA_ENABLE -void _tx_vfp_access(void); - -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = *((ULONG *) 0xE000EF34); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_vfp_access(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _get_control(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _set_control(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + TX_VFP_TOUCH(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #else -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ - ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ - if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - else \ - { \ - ULONG _tx_fpccr; \ - _tx_fpccr = _tx_misra_fpccr_get(); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ - if (_tx_fpccr == ((ULONG) 0x01)) \ - { \ - ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ - _tx_misra_vfp_touch(); \ - if (_tx_vfp_state == ((ULONG) 0)) \ - { \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - _tx_misra_control_set(_tx_vfp_state); \ - } \ - } \ - } \ - } +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } #endif -#else +#else /* No VFP in use */ #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -490,16 +544,27 @@ void _tx_vfp_access(void); #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Define the get system state macro. */ - +/* Define the get system state macro. */ + #ifndef TX_THREAD_GET_SYSTEM_STATE #ifndef TX_MISRA_ENABLE -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _get_ipsr()) -#else + +#if defined(__GNUC__) /* GCC and AC6 */ +__attribute__( ( always_inline ) ) static inline UINT _tx_ipsr_get(void) +{ +UINT ipsr_value; + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} +#endif /* GCC and AC6 IPSR_get function. */ + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_ipsr_get()) + +#else /* TX_MISRA_ENABLE is defined, use MISRA function. */ ULONG _tx_misra_ipsr_get(VOID); #define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) -#endif -#endif +#endif /* TX_MISRA_ENABLE */ +#endif /* TX_THREAD_GET_SYSTEM_STATE */ /* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value @@ -517,31 +582,102 @@ extern void _tx_thread_secure_stack_initialize(void); #define TX_INITIALIZE_KERNEL_ENTER_EXTENSION _tx_thread_secure_stack_initialize(); #endif -/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to prevent early scheduling on Cortex-M parts. */ - + #define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; -/* Determine if the ARM architecture has the CLZ instruction. This is available on - architectures v5 and above. If available, redefine the macro for calculating the - lowest bit set. */ + #ifndef TX_DISABLE_INLINE -#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT)__clz(__rbit((m))); - +/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */ +#ifdef __ICCARM__ /* IAR Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); +#elif defined(__GNUC__) /* GCC and AC6 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#else +#error "Compiler not supported." #endif -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ -#ifdef TX_DISABLE_INLINE +/* Define the interrupt disable/restore macros. */ + +__attribute__( ( always_inline ) ) static inline UINT __get_interrupt_posture(void) +{ +UINT posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(UINT basepri_value) +{ + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(UINT int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif +} + +__attribute__( ( always_inline ) ) static inline UINT __disable_interrupts(void) +{ +UINT int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); +} + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +UINT interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (_tx_ipsr_get() == 0) + { + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif + __restore_interrupt(interrupt_save); + } +} + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/* Redefine _tx_thread_system_return for improved performance. */ +#define _tx_thread_system_return _tx_thread_system_return_inline + +#else /* TX_DISABLE_INLINE is defined */ UINT _tx_thread_interrupt_disable(VOID); VOID _tx_thread_interrupt_restore(UINT previous_posture); @@ -549,41 +685,14 @@ VOID _tx_thread_interrupt_restore(UIN #define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; #define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); - #define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); - -#else - -#define TX_INTERRUPT_SAVE_AREA UINT was_masked; -#define TX_DISABLE was_masked = __disable_irq(); -#define TX_RESTORE if (was_masked == 0) __enable_irq(); - -#define _tx_thread_system_return _tx_thread_system_return_inline - - -static void _tx_thread_system_return_inline(void) -{ -unsigned int was_masked; - - - /* Set PendSV to invoke ThreadX scheduler. */ - *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (_get_ipsr() == 0) - { - was_masked = __disable_irq(); - __enable_irq(); - if (was_masked != 0) - __disable_irq(); - } -} -#endif - +#endif /* TX_DISABLE_INLINE */ /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT -CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33/AC6 Version 6.1.9 *"; +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33 Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; @@ -592,8 +701,4 @@ extern CHAR _tx_version_id[]; #endif #endif - #endif - - - diff --git a/ports_module/cortex_m33/ac6/inc/tx_secure_interface.h b/ports_module/cortex_m33/ac6/inc/tx_secure_interface.h index bcbd3cae..d6fba106 100644 --- a/ports_module/cortex_m33/ac6/inc/tx_secure_interface.h +++ b/ports_module/cortex_m33/ac6/inc/tx_secure_interface.h @@ -26,7 +26,7 @@ /* COMPONENT DEFINITION RELEASE */ /* */ /* tx_secure_interface.h PORTABLE C */ -/* 6.1.3 */ +/* 6.1 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -51,9 +51,10 @@ /* Define internal secure thread stack function prototypes. */ -extern void _tx_thread_secure_stack_initialize(void); +extern UINT _tx_thread_secure_mode_stack_initialize(void); extern UINT _tx_thread_secure_mode_stack_allocate(TX_THREAD *thread_ptr, ULONG stack_size); extern UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr); +extern void _tx_thread_secure_stack_initialize(void); extern void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr); extern void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr); diff --git a/ports_module/cortex_m33/ac6/inc/txm_module_port.h b/ports_module/cortex_m33/ac6/inc/txm_module_port.h index 7c5cd693..b3b114b1 100644 --- a/ports_module/cortex_m33/ac6/inc/txm_module_port.h +++ b/ports_module/cortex_m33/ac6/inc/txm_module_port.h @@ -25,8 +25,8 @@ /* */ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ -/* txm_module_port.h Cortex-M33/MPU/AC6 */ -/* 6.1.3 */ +/* txm_module_port.h Cortex-M33/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 12-31-2020 Scott Larson Initial Version 6.1.3 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user-configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -90,6 +93,11 @@ The following extensions must also be defined in tx_port.h: VOID (*tx_timer_module_expiration_function)(ULONG id); */ +/* Users can define the module heap size. */ +#ifndef TXM_MODULE_HEAP_SIZE +#define TXM_MODULE_HEAP_SIZE 512 +#endif + /* Define the kernel stack size for a module thread. */ #ifndef TXM_MODULE_KERNEL_STACK_SIZE #define TXM_MODULE_KERNEL_STACK_SIZE 768 @@ -349,6 +357,6 @@ ALIGN_TYPE _txm_module_manager_port_dispatch(TXM_MODULE_INSTANCE *module_instanc #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M33/MPU/AC6 Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M33/AC6 Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m33/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_m33/ac6/module_lib/src/txm_module_initialize.S index a18327b2..ea9c72e7 100644 --- a/ports_module/cortex_m33/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_m33/ac6/module_lib/src/txm_module_initialize.S @@ -32,8 +32,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _txm_module_initialize Cortex-M33/MPU/AC6 */ -/* 6.1.3 */ +/* _txm_module_initialize Cortex-M33/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -62,15 +62,18 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 12-31-2020 Scott Larson Initial Version 6.1.3 */ +/* 12-31-2020 Scott Larson Initial Version 6.1.3 */ +/* 01-31-2022 Scott Larson Modified comments, fixed */ +/* scatterload, and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -// VOID _txm_module_initialize(VOID) .global _txm_module_initialize .thumb_func _txm_module_initialize: - PUSH {r4-r12,lr} // Save dregs and LR - //B __scatterload // Call ARM func to initialize variables + PUSH {r0-r12,lr} // Save dregs and LR + B __scatterload // Call ARM func to initialize variables // Override the __rt_exit function. .global __rt_exit @@ -79,25 +82,11 @@ __rt_exit: POP {r4-r12,lr} // Restore dregs and LR BX lr // Return to caller -#define TXM_MODULE_HEAP_SIZE 512 - -// returns heap start address in R0 -// returns heap end address in R2 -// does not touch SP, it is already set up before the module runs - .global __user_setup_stackheap - .thumb_func -__user_setup_stackheap: - LDR r1, _txm_heap // load heap offset - MOV r2, TXM_MODULE_HEAP_SIZE // load heap size - ADD r2, r2, r0 // calculate heap end address - BX lr - -// dummy main function - .global main - .thumb_func -main: - BX lr - - .align 8 -_txm_heap: - .zero TXM_MODULE_HEAP_SIZE +// Override the __rt_entry function. + .global __rt_entry + .type __rt_entry, %function +__rt_entry: + POP {r0-r1} + BL __rt_lib_init // Call ARM func to initialize library + POP {r2-r12,lr} // Restore dregs and LR + BX lr // Return to caller diff --git a/ports_module/cortex_m33/ac6/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m33/ac6/module_lib/src/txm_module_thread_shell_entry.c index 55bb7e7d..ad2c6205 100644 --- a/ports_module/cortex_m33/ac6/module_lib/src/txm_module_thread_shell_entry.c +++ b/ports_module/cortex_m33/ac6/module_lib/src/txm_module_thread_shell_entry.c @@ -28,12 +28,13 @@ #define TX_SOURCE_CODE #endif - /* Include necessary system files. */ #include "txm_module.h" #include "tx_thread.h" +__asm(".global __ARM_use_no_argv"); + /* Define the global module entry pointer from the start thread of the module. */ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; @@ -44,23 +45,27 @@ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param3); -/* Define the ARM cstartup code. */ -extern VOID _txm_module_initialize(VOID); +/* Define the module's heap and align it to 8 bytes. */ +__attribute__((aligned(8))) UCHAR txm_heap[TXM_MODULE_HEAP_SIZE]; + + +/* Use our asm routine that calls the ARM code to initialize data and heap. */ +extern VOID _txm_module_initialize(VOID *heap_base, VOID *heap_top); /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _txm_module_thread_shell_entry Cortex-M33/MPU/AC6 */ -/* 6.1.3 */ +/* _txm_module_thread_shell_entry Cortex-M33/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function calls the specified entry function of the thread. It */ +/* This function calls the specified entry function of the thread. It */ /* also provides a place for the thread's entry function to return. */ /* If the thread returns, this function places the thread in a */ /* "COMPLETED" state. */ @@ -90,6 +95,10 @@ extern VOID _txm_module_initialize(VOID); /* DATE NAME DESCRIPTION */ /* */ /* 12-31-2020 Scott Larson Initial Version 6.1.3 */ +/* 01-31-2022 Scott Larson Modified comments, fixed */ +/* scatterload, and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) @@ -99,21 +108,20 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN VOID (*entry_exit_notify)(TX_THREAD *, UINT); #endif - /* Determine if this is the start thread. If so, we must prepare the module for execution. If not, simply skip the C startup code. */ if (thread_info -> txm_module_thread_entry_info_start_thread) { - /* Initialize the ARM C environment. */ - _txm_module_initialize(); - + /* Initialize the C environment. */ + _txm_module_initialize(&txm_heap[0], &txm_heap[TXM_MODULE_HEAP_SIZE-1]); + /* Save the entry info pointer, for later use. */ _txm_module_entry_info = thread_info; - + /* Save the kernel function dispatch address. This is used to make all resident calls from the module. */ _txm_module_kernel_call_dispatcher = thread_info -> txm_module_thread_entry_info_kernel_call_dispatcher; - + /* Ensure that we have a valid pointer. */ while (!_txm_module_kernel_call_dispatcher) { @@ -169,4 +177,3 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); #endif } - diff --git a/ports_module/cortex_m33/gnu/inc/tx_port.h b/ports_module/cortex_m33/gnu/inc/tx_port.h index ade416e5..eb4a7f5f 100644 --- a/ports_module/cortex_m33/gnu/inc/tx_port.h +++ b/ports_module/cortex_m33/gnu/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h ARMv8-M */ -/* 6.1.5 */ +/* tx_port.h Cortex-M33 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -43,28 +43,45 @@ /* own special types that can be mapped to actual data types by this */ /* file to guarantee consistency in the interface and functionality. */ /* */ +/* This file replaces the previous Cortex-M33 files. It unifies */ +/* the Cortex-M33 compilers into one common file. */ +/* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 03-02-2021 Scott Larson Initial Version 6.1.5 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 03-02-2021 Scott Larson Modified comment(s), added */ +/* ULONG64_DEFINED, */ +/* resulting in version 6.1.5 */ +/* 06-02-2021 Scott Larson Modified comment(s), removed */ +/* unneeded header file, funcs */ +/* set_control and get_control */ +/* changed to inline, */ +/* added symbol to enable */ +/* stack error handler, */ +/* resulting in version 6.1.7 */ +/* 10-15-2021 Scott Larson Modified comment(s), improved */ +/* stack check error handling, */ +/* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), unified */ +/* this file across compilers, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ #ifndef TX_PORT_H #define TX_PORT_H - /* Determine if the optional ThreadX user define file should be used. */ - #ifdef TX_INCLUDE_USER_DEFINE_FILE /* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" -#endif - +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ /* Define compiler library include files. */ @@ -72,13 +89,20 @@ #include #ifdef __ICCARM__ -#include /* IAR Intrinsics */ -#define __asm__ __asm /* Define to make all inline asm look similar */ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm from each compiler look similar */ +#define _tx_control_get __get_CONTROL +#define _tx_control_set __set_CONTROL +#define _tx_ipsr_get __get_IPSR #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT #include -#endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #endif /* __ICCARM__ */ +#ifdef __ARMCOMPILER_VERSION +#include +#endif + /* Define ThreadX basic types for this port. */ @@ -101,35 +125,17 @@ UINT _txe_thread_secure_stack_free(struct TX_THREAD_STRUCT *thread_ptr); UINT _tx_thread_secure_stack_allocate(struct TX_THREAD_STRUCT *tx_thread, ULONG stack_size); UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); -/* This port overrides tx_thread_stack_error_notify with an architecture specific version */ -#define TX_PORT_THREAD_STACK_ERROR_NOTIFY - -/* This port overrides tx_thread_stack_error_handler with an architecture specific version */ -#define TX_PORT_THREAD_STACK_ERROR_HANDLER - -/* This hardware has stack checking that we take advantage of - do NOT define. */ -#ifdef TX_ENABLE_STACK_CHECKING - #error "Do not define TX_ENABLE_STACK_CHECKING" -#endif - -/* If user does not want to terminate thread on stack overflow, - #define the TX_THREAD_NO_TERMINATE_STACK_ERROR symbol. - The thread will be rescheduled and continue to cause the exception. - It is suggested user code handle this by registering a notification with the - tx_thread_stack_error_notify function. */ -/*#define TX_THREAD_NO_TERMINATE_STACK_ERROR */ - -/* Define the system API mappings based on the error checking - selected by the user. Note: this section is only applicable to +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to application source code, hence the conditional that turns off this stuff when the include file is processed by the ThreadX source. */ #ifndef TX_SOURCE_CODE -/* Determine if error checking is desired. If so, map API functions +/* Determine if error checking is desired. If so, map API functions to the appropriate error checking front-ends. Otherwise, map API - functions to the core functions that actually perform the work. + functions to the core functions that actually perform the work. Note: error checking is enabled by default. */ #ifdef TX_DISABLE_ERROR_CHECKING @@ -146,10 +152,11 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define tx_thread_secure_stack_allocate _txe_thread_secure_stack_allocate #define tx_thread_secure_stack_free _txe_thread_secure_stack_free -#endif -#endif - +#endif /* TX_DISABLE_ERROR_CHECKING */ +#endif /* TX_SOURCE_CODE */ +/* This port has a usage fault handler in _tx_initialize_low_level for stack exceptions. */ +#define TX_PORT_THREAD_STACK_ERROR_HANDLING /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ @@ -244,84 +251,82 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_THREAD_EXTENSION_0 #define TX_THREAD_EXTENSION_1 + #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT /* IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* ThreadX in non-secure zone with calls to secure zone. */ -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_secure_stack_context; \ - VOID *tx_thread_iar_tls_pointer; +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_secure_stack_context; \ + VOID *tx_thread_iar_tls_pointer; #else -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_iar_tls_pointer; +/* ThreadX in only one zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_iar_tls_pointer; #endif #else /* No IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* ThreadX in non-secure zone with calls to secure zone. */ -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_secure_stack_context; +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_secure_stack_context; #else /* ThreadX in only one zone. */ -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; #endif -#endif -#ifndef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + #define TX_THREAD_EXTENSION_3 -#else -#define TX_THREAD_EXTENSION_3 unsigned long long tx_thread_execution_time_total; \ - unsigned long long tx_thread_execution_time_last_start; -#endif /* Define the port extensions of the remaining ThreadX objects. */ @@ -352,24 +357,22 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, tx_thread_shell_entry, and tx_thread_terminate. */ - - #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT void *_tx_iar_create_per_thread_tls_area(void); void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); void __iar_Initlocks(void); - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) #define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ - thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ - thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); #endif #define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); + #else /* No IAR library support. */ #define TX_THREAD_CREATE_EXTENSION(thread_ptr) #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) @@ -385,7 +388,8 @@ void __iar_Initlocks(void); #define TX_TIMER_INITIALIZE_EXTENSION(status) _tx_thread_secure_stack_allocate(&_tx_timer_thread, TX_TIMER_THREAD_SECURE_STACK_SIZE); #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) + +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -396,9 +400,9 @@ void _tx_misra_vfp_touch(void); #else /* TX_MISRA_ENABLE not defined */ -/* Define some helper functions (these are intrinsics in some compilers). */ -#ifdef __GNUC__ -__attribute__( ( always_inline ) ) static inline ULONG __get_CONTROL(void) +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline ULONG _tx_control_get(void) { ULONG control_value; @@ -406,22 +410,18 @@ ULONG control_value; return(control_value); } -__attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG control_value) +__attribute__( ( always_inline ) ) static inline void _tx_control_set(ULONG control_value) { __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); } -#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); +#endif /* __GNUC__ */ -#endif /* __GNUC__ */ - -#ifdef __ICCARM__ +/* Touch VFP register in order to flush. Works for AC6/GCC/IAR compilers. */ #define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); -#endif /* __ICCARM__ */ #endif /* TX_MISRA_ENABLE */ - /* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA in order to ensure no lazy stacking will occur. */ @@ -429,54 +429,53 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ } #else #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ _tx_misra_control_set(_tx_vfp_state); \ } - #endif /* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating - this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush the lazy FPU save, then restore the CONTROL.FPCA state. */ #ifndef TX_MISRA_ENABLE #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ } \ else \ { \ ULONG _tx_fpccr; \ - _tx_fpccr = *((ULONG *) 0xE000EF34); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ } \ } \ } \ @@ -485,29 +484,29 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ _tx_misra_control_set(_tx_vfp_state); \ } \ else \ { \ ULONG _tx_fpccr; \ - _tx_fpccr = _tx_misra_fpccr_get(); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ _tx_misra_vfp_touch(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ _tx_misra_control_set(_tx_vfp_state); \ } \ } \ @@ -520,7 +519,7 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -545,27 +544,21 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Define the get system state macro. */ +/* Define the get system state macro. */ #ifndef TX_THREAD_GET_SYSTEM_STATE #ifndef TX_MISRA_ENABLE -#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ - -__attribute__( ( always_inline ) ) static inline unsigned int __get_IPSR(void) +#if defined(__GNUC__) /* GCC and AC6 */ +__attribute__( ( always_inline ) ) static inline UINT _tx_ipsr_get(void) { -unsigned int ipsr_value; +UINT ipsr_value; __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); return(ipsr_value); } +#endif /* GCC and AC6 IPSR_get function. */ -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) - -#elif defined(__ICCARM__) /* IAR */ - -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) - -#endif /* TX_THREAD_GET_SYSTEM_STATE for different compilers */ +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_ipsr_get()) #else /* TX_MISRA_ENABLE is defined, use MISRA function. */ ULONG _tx_misra_ipsr_get(VOID); @@ -604,87 +597,86 @@ extern void _tx_thread_secure_stack_initialize(void); #define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); #elif defined(__GNUC__) /* GCC and AC6 Compiler */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ - __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#else +#error "Compiler not supported." #endif -/* Define the interrupt disable/restore macros for each compiler. */ -#ifdef __GNUC__ /* GCC and AC6 */ -__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupt(void) +/* Define the interrupt disable/restore macros. */ + +__attribute__( ( always_inline ) ) static inline UINT __get_interrupt_posture(void) { -unsigned int primask_value; - - __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); - __asm__ volatile (" CPSID i" : : : "memory" ); - return(primask_value); +UINT posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); } -__attribute__( ( always_inline ) ) static inline void __restore_interrupt(unsigned int primask_value) +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(UINT basepri_value) { - __asm__ volatile (" MSR PRIMASK,%0": : "r" (primask_value): "memory" ); + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(UINT int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif } -__attribute__( ( always_inline ) ) static inline unsigned int __get_primask_value(void) +__attribute__( ( always_inline ) ) static inline UINT __disable_interrupts(void) { -unsigned int primask_value; +UINT int_posture; - __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); - return(primask_value); + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); } -__attribute__( ( always_inline ) ) static inline void __enable_interrupt(void) -{ - __asm__ volatile (" CPSIE i": : : "memory" ); -} - - __attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) { -unsigned int interrupt_save; +UINT interrupt_save; /* Set PendSV to invoke ThreadX scheduler. */ *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (__get_IPSR() == 0) + if (_tx_ipsr_get() == 0) { - interrupt_save = __get_primask_value(); - __enable_interrupt(); + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif __restore_interrupt(interrupt_save); } } - #define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; -#define TX_DISABLE interrupt_save = __disable_interrupt(); +#define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); -#elif defined(__ICCARM__) /* IAR */ - -static void _tx_thread_system_return_inline(void) -{ -__istate_t interrupt_save; - - /* Set PendSV to invoke ThreadX scheduler. */ - *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (__get_IPSR() == 0) - { - interrupt_save = __get_interrupt_state(); - __enable_interrupt(); - __set_interrupt_state(interrupt_save); - } -} - -#define TX_INTERRUPT_SAVE_AREA __istate_t interrupt_save; -#define TX_DISABLE {interrupt_save = __get_interrupt_state();__disable_interrupt();}; -#define TX_RESTORE {__set_interrupt_state(interrupt_save);}; - -#endif /* Interrupt disable/restore macros for each compiler. */ - /* Redefine _tx_thread_system_return for improved performance. */ - #define _tx_thread_system_return _tx_thread_system_return_inline - #else /* TX_DISABLE_INLINE is defined */ UINT _tx_thread_interrupt_disable(VOID); @@ -696,12 +688,11 @@ VOID _tx_thread_interrupt_restore(UIN #define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); #endif /* TX_DISABLE_INLINE */ - /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-M Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33 Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; @@ -710,5 +701,4 @@ extern CHAR _tx_version_id[]; #endif #endif - #endif diff --git a/ports_module/cortex_m33/gnu/inc/tx_secure_interface.h b/ports_module/cortex_m33/gnu/inc/tx_secure_interface.h index 0a2bdd67..d6fba106 100644 --- a/ports_module/cortex_m33/gnu/inc/tx_secure_interface.h +++ b/ports_module/cortex_m33/gnu/inc/tx_secure_interface.h @@ -51,9 +51,10 @@ /* Define internal secure thread stack function prototypes. */ -extern void _tx_thread_secure_stack_initialize(void); +extern UINT _tx_thread_secure_mode_stack_initialize(void); extern UINT _tx_thread_secure_mode_stack_allocate(TX_THREAD *thread_ptr, ULONG stack_size); extern UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr); +extern void _tx_thread_secure_stack_initialize(void); extern void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr); extern void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr); diff --git a/ports_module/cortex_m33/gnu/inc/txm_module_port.h b/ports_module/cortex_m33/gnu/inc/txm_module_port.h index 7ae91c24..1c49473b 100644 --- a/ports_module/cortex_m33/gnu/inc/txm_module_port.h +++ b/ports_module/cortex_m33/gnu/inc/txm_module_port.h @@ -25,8 +25,8 @@ /* */ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ -/* txm_module_port.h Cortex-M33/MPU/GNU */ -/* 6.1.5 */ +/* txm_module_port.h Cortex-M33/GNU */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 03-02-2021 Scott Larson Initial Version 6.1.5 */ +/* 01-31-2022 Scott Larson Modified comment(s), */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -349,6 +351,6 @@ ALIGN_TYPE _txm_module_manager_port_dispatch(TXM_MODULE_INSTANCE *module_instanc #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M33/MPU/GNU Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M33/GNU Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_initialize_low_level.S b/ports_module/cortex_m33/gnu/module_manager/src/tx_initialize_low_level.S index 3f7cc108..b3910b01 100644 --- a/ports_module/cortex_m33/gnu/module_manager/src/tx_initialize_low_level.S +++ b/ports_module/cortex_m33/gnu/module_manager/src/tx_initialize_low_level.S @@ -34,7 +34,7 @@ HEAP_SIZE = 0x00000000 /* FUNCTION RELEASE */ /* */ /* _tx_initialize_low_level Cortex-M33/GNU */ -/* 6.1 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -68,6 +68,8 @@ HEAP_SIZE = 0x00000000 /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_initialize_low_level(VOID) @@ -117,11 +119,9 @@ _tx_initialize_low_level: /* 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 @@ -154,12 +154,16 @@ __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 + BX lr // } @@ -174,9 +178,15 @@ 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 + BX lr // } @@ -210,7 +220,7 @@ _unhandled_usage_loop: // Handle stack overflow STR r1, [r0] // Clear CFSR flag(s) -#ifdef __ARM_PCS_VFP +#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 @@ -223,7 +233,7 @@ _unhandled_usage_loop: BL _tx_thread_stack_error_handler // Call ThreadX/user handler POP {r0,lr} // Restore LR and dummy reg -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#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 diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_schedule.S index a23118c4..034f8b17 100644 --- a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_schedule.S +++ b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_schedule.S @@ -29,7 +29,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M33/MPU/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -68,6 +68,8 @@ /* 06-02-2021 Scott Larson Fixed extended stack handling */ /* when calling kernel APIs, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -89,7 +91,7 @@ _tx_thread_schedule: LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag STR r0, [r2, #0] // Clear preempt disable flag -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit @@ -185,7 +187,7 @@ BusFault_Handler: // Bit 7 = 1 -> MMFAR is valid STRB r1, [r0] // Clear the MMFSR -#ifdef __ARM_PCS_VFP +#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 @@ -255,7 +257,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -397,7 +399,7 @@ _skip_secure_restore: STR r1, [r0] // Enable MPU skip_mpu_setup: LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers diff --git a/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_secure_stack_initialize.S b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_secure_stack_initialize.S new file mode 100644 index 00000000..9e9642fc --- /dev/null +++ b/ports_module/cortex_m33/gnu/module_manager/src/tx_thread_secure_stack_initialize.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_secure_stack_initialize Cortex-M33/GNU */ +/* 6.1.7 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enters the SVC handler to initialize a secure stack. */ +/* */ +/* INPUT */ +/* */ +/* none */ +/* */ +/* OUTPUT */ +/* */ +/* none */ +/* */ +/* CALLS */ +/* */ +/* SVC 3 */ +/* */ +/* CALLED BY */ +/* */ +/* TX_INITIALIZE_KERNEL_ENTER_EXTENSION */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_secure_stack_initialize(VOID) +// { + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global _tx_thread_secure_stack_initialize + .thumb_func +.type _tx_thread_secure_stack_initialize, function +_tx_thread_secure_stack_initialize: +#if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) + CPSIE i // Enable interrupts for SVC call + SVC 3 + CPSID i // Disable interrupts +#else + MOV r0, #0xFF // Feature not enabled +#endif + BX lr + .end diff --git a/ports_module/cortex_m33/iar/inc/tx_port.h b/ports_module/cortex_m33/iar/inc/tx_port.h index 487bc71b..eb4a7f5f 100644 --- a/ports_module/cortex_m33/iar/inc/tx_port.h +++ b/ports_module/cortex_m33/iar/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h ARMv8-M */ -/* 6.1.5 */ +/* tx_port.h Cortex-M33 */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -43,28 +43,45 @@ /* own special types that can be mapped to actual data types by this */ /* file to guarantee consistency in the interface and functionality. */ /* */ +/* This file replaces the previous Cortex-M33 files. It unifies */ +/* the Cortex-M33 compilers into one common file. */ +/* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 03-02-2021 Scott Larson Initial Version 6.1.5 */ +/* 09-30-2020 Scott Larson Initial Version 6.1 */ +/* 03-02-2021 Scott Larson Modified comment(s), added */ +/* ULONG64_DEFINED, */ +/* resulting in version 6.1.5 */ +/* 06-02-2021 Scott Larson Modified comment(s), removed */ +/* unneeded header file, funcs */ +/* set_control and get_control */ +/* changed to inline, */ +/* added symbol to enable */ +/* stack error handler, */ +/* resulting in version 6.1.7 */ +/* 10-15-2021 Scott Larson Modified comment(s), improved */ +/* stack check error handling, */ +/* resulting in version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comment(s), unified */ +/* this file across compilers, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ #ifndef TX_PORT_H #define TX_PORT_H - /* Determine if the optional ThreadX user define file should be used. */ - #ifdef TX_INCLUDE_USER_DEFINE_FILE /* Yes, include the user defines in tx_user.h. The defines in this file may alternately be defined on the command line. */ #include "tx_user.h" -#endif - +#endif /* TX_INCLUDE_USER_DEFINE_FILE */ /* Define compiler library include files. */ @@ -72,13 +89,20 @@ #include #ifdef __ICCARM__ -#include /* IAR Intrinsics */ -#define __asm__ __asm /* Define to make all inline asm look similar */ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm from each compiler look similar */ +#define _tx_control_get __get_CONTROL +#define _tx_control_set __set_CONTROL +#define _tx_ipsr_get __get_IPSR #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT #include -#endif +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ #endif /* __ICCARM__ */ +#ifdef __ARMCOMPILER_VERSION +#include +#endif + /* Define ThreadX basic types for this port. */ @@ -101,35 +125,17 @@ UINT _txe_thread_secure_stack_free(struct TX_THREAD_STRUCT *thread_ptr); UINT _tx_thread_secure_stack_allocate(struct TX_THREAD_STRUCT *tx_thread, ULONG stack_size); UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); -/* This port overrides tx_thread_stack_error_notify with an architecture specific version */ -#define TX_PORT_THREAD_STACK_ERROR_NOTIFY - -/* This port overrides tx_thread_stack_error_handler with an architecture specific version */ -#define TX_PORT_THREAD_STACK_ERROR_HANDLER - -/* This hardware has stack checking that we take advantage of - do NOT define. */ -#ifdef TX_ENABLE_STACK_CHECKING - #error "Do not define TX_ENABLE_STACK_CHECKING" -#endif - -/* If user does not want to terminate thread on stack overflow, - #define the TX_THREAD_NO_TERMINATE_STACK_ERROR symbol. - The thread will be rescheduled and continue to cause the exception. - It is suggested user code handle this by registering a notification with the - tx_thread_stack_error_notify function. */ -/*#define TX_THREAD_NO_TERMINATE_STACK_ERROR */ - -/* Define the system API mappings based on the error checking - selected by the user. Note: this section is only applicable to +/* Define the system API mappings based on the error checking + selected by the user. Note: this section is only applicable to application source code, hence the conditional that turns off this stuff when the include file is processed by the ThreadX source. */ #ifndef TX_SOURCE_CODE -/* Determine if error checking is desired. If so, map API functions +/* Determine if error checking is desired. If so, map API functions to the appropriate error checking front-ends. Otherwise, map API - functions to the core functions that actually perform the work. + functions to the core functions that actually perform the work. Note: error checking is enabled by default. */ #ifdef TX_DISABLE_ERROR_CHECKING @@ -146,10 +152,11 @@ UINT _tx_thread_secure_stack_free(struct TX_THREAD_STRUCT *tx_thread); #define tx_thread_secure_stack_allocate _txe_thread_secure_stack_allocate #define tx_thread_secure_stack_free _txe_thread_secure_stack_free -#endif -#endif - +#endif /* TX_DISABLE_ERROR_CHECKING */ +#endif /* TX_SOURCE_CODE */ +/* This port has a usage fault handler in _tx_initialize_low_level for stack exceptions. */ +#define TX_PORT_THREAD_STACK_ERROR_HANDLING /* Define the priority levels for ThreadX. Legal values range from 32 to 1024 and MUST be evenly divisible by 32. */ @@ -244,84 +251,82 @@ ULONG _tx_misra_time_stamp_get(VOID); #define TX_THREAD_EXTENSION_0 #define TX_THREAD_EXTENSION_1 + #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT /* IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* ThreadX in non-secure zone with calls to secure zone. */ -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_secure_stack_context; \ - VOID *tx_thread_iar_tls_pointer; +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_secure_stack_context; \ + VOID *tx_thread_iar_tls_pointer; #else -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_iar_tls_pointer; +/* ThreadX in only one zone. */ +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_iar_tls_pointer; #endif #else /* No IAR library support */ #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) /* ThreadX in non-secure zone with calls to secure zone. */ -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; \ - VOID *tx_thread_secure_stack_context; +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; \ + VOID *tx_thread_secure_stack_context; #else /* ThreadX in only one zone. */ -#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ - VOID *tx_thread_module_entry_info_ptr; \ - ULONG tx_thread_module_current_user_mode; \ - ULONG tx_thread_module_user_mode; \ - ULONG tx_thread_module_saved_lr; \ - VOID *tx_thread_module_kernel_stack_start; \ - VOID *tx_thread_module_kernel_stack_end; \ - ULONG tx_thread_module_kernel_stack_size; \ - VOID *tx_thread_module_stack_ptr; \ - VOID *tx_thread_module_stack_start; \ - VOID *tx_thread_module_stack_end; \ - ULONG tx_thread_module_stack_size; \ - VOID *tx_thread_module_reserved; +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_module_instance_ptr; \ + VOID *tx_thread_module_entry_info_ptr; \ + ULONG tx_thread_module_current_user_mode; \ + ULONG tx_thread_module_user_mode; \ + ULONG tx_thread_module_saved_lr; \ + VOID *tx_thread_module_kernel_stack_start; \ + VOID *tx_thread_module_kernel_stack_end; \ + ULONG tx_thread_module_kernel_stack_size; \ + VOID *tx_thread_module_stack_ptr; \ + VOID *tx_thread_module_stack_start; \ + VOID *tx_thread_module_stack_end; \ + ULONG tx_thread_module_stack_size; \ + VOID *tx_thread_module_reserved; #endif -#endif -#ifndef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#endif /* TX_ENABLE_IAR_LIBRARY_SUPPORT */ + #define TX_THREAD_EXTENSION_3 -#else -#define TX_THREAD_EXTENSION_3 unsigned long long tx_thread_execution_time_total; \ - unsigned long long tx_thread_execution_time_last_start; -#endif /* Define the port extensions of the remaining ThreadX objects. */ @@ -352,24 +357,22 @@ ULONG _tx_misra_time_stamp_get(VOID); /* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, tx_thread_shell_entry, and tx_thread_terminate. */ - - #ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT void *_tx_iar_create_per_thread_tls_area(void); void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); void __iar_Initlocks(void); - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) #define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ - thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); \ if(thread_ptr -> tx_thread_secure_stack_context){_tx_thread_secure_stack_free(thread_ptr);} #else #define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ - thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); #endif #define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); + #else /* No IAR library support. */ #define TX_THREAD_CREATE_EXTENSION(thread_ptr) #if !defined(TX_SINGLE_MODE_SECURE) && !defined(TX_SINGLE_MODE_NON_SECURE) @@ -385,7 +388,8 @@ void __iar_Initlocks(void); #define TX_TIMER_INITIALIZE_EXTENSION(status) _tx_thread_secure_stack_allocate(&_tx_timer_thread, TX_TIMER_THREAD_SECURE_STACK_SIZE); #endif -#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) + +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) #ifdef TX_MISRA_ENABLE @@ -396,9 +400,9 @@ void _tx_misra_vfp_touch(void); #else /* TX_MISRA_ENABLE not defined */ -/* Define some helper functions (these are intrinsics in some compilers). */ -#ifdef __GNUC__ -__attribute__( ( always_inline ) ) static inline ULONG __get_CONTROL(void) +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline ULONG _tx_control_get(void) { ULONG control_value; @@ -406,22 +410,18 @@ ULONG control_value; return(control_value); } -__attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG control_value) +__attribute__( ( always_inline ) ) static inline void _tx_control_set(ULONG control_value) { __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); } -#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); +#endif /* __GNUC__ */ -#endif /* __GNUC__ */ - -#ifdef __ICCARM__ +/* Touch VFP register in order to flush. Works for AC6/GCC/IAR compilers. */ #define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); -#endif /* __ICCARM__ */ #endif /* TX_MISRA_ENABLE */ - /* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA in order to ensure no lazy stacking will occur. */ @@ -429,54 +429,53 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ } #else #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ _tx_misra_control_set(_tx_vfp_state); \ } - #endif /* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating - this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush the lazy FPU save, then restore the CONTROL.FPCA state. */ #ifndef TX_MISRA_ENABLE #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ } \ else \ { \ ULONG _tx_fpccr; \ - _tx_fpccr = *((ULONG *) 0xE000EF34); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + _tx_fpccr = *((ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ TX_VFP_TOUCH(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = __get_CONTROL(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ - __set_CONTROL(_tx_vfp_state); \ + _tx_vfp_state = _tx_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_control_set(_tx_vfp_state); \ } \ } \ } \ @@ -485,29 +484,29 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ ULONG _tx_system_state; \ - _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ { \ ULONG _tx_vfp_state; \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ _tx_misra_control_set(_tx_vfp_state); \ } \ else \ { \ ULONG _tx_fpccr; \ - _tx_fpccr = _tx_misra_fpccr_get(); \ - _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ if (_tx_fpccr == ((ULONG) 0x01)) \ { \ ULONG _tx_vfp_state; \ _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ _tx_misra_vfp_touch(); \ if (_tx_vfp_state == ((ULONG) 0)) \ { \ - _tx_vfp_state = _tx_misra_control_get(); \ - _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ _tx_misra_control_set(_tx_vfp_state); \ } \ } \ @@ -520,7 +519,7 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) #define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) -#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__TARGET_FPU_VFP) */ +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ /* Define the ThreadX object creation extensions for the remaining objects. */ @@ -545,27 +544,21 @@ __attribute__( ( always_inline ) ) static inline void __set_CONTROL(ULONG contro #define TX_TIMER_DELETE_EXTENSION(timer_ptr) -/* Define the get system state macro. */ +/* Define the get system state macro. */ #ifndef TX_THREAD_GET_SYSTEM_STATE #ifndef TX_MISRA_ENABLE -#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ - -__attribute__( ( always_inline ) ) static inline unsigned int __get_IPSR(void) +#if defined(__GNUC__) /* GCC and AC6 */ +__attribute__( ( always_inline ) ) static inline UINT _tx_ipsr_get(void) { -unsigned int ipsr_value; +UINT ipsr_value; __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); return(ipsr_value); } +#endif /* GCC and AC6 IPSR_get function. */ -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) - -#elif defined(__ICCARM__) /* IAR */ - -#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) - -#endif /* TX_THREAD_GET_SYSTEM_STATE for different compilers */ +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_ipsr_get()) #else /* TX_MISRA_ENABLE is defined, use MISRA function. */ ULONG _tx_misra_ipsr_get(VOID); @@ -604,87 +597,86 @@ extern void _tx_thread_secure_stack_initialize(void); #define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); #elif defined(__GNUC__) /* GCC and AC6 Compiler */ #define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ - __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#else +#error "Compiler not supported." #endif -/* Define the interrupt disable/restore macros for each compiler. */ -#ifdef __GNUC__ /* GCC and AC6 */ -__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupt(void) +/* Define the interrupt disable/restore macros. */ + +__attribute__( ( always_inline ) ) static inline UINT __get_interrupt_posture(void) { -unsigned int primask_value; - - __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); - __asm__ volatile (" CPSID i" : : : "memory" ); - return(primask_value); +UINT posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); } -__attribute__( ( always_inline ) ) static inline void __restore_interrupt(unsigned int primask_value) +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(UINT basepri_value) { - __asm__ volatile (" MSR PRIMASK,%0": : "r" (primask_value): "memory" ); + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(UINT int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif } -__attribute__( ( always_inline ) ) static inline unsigned int __get_primask_value(void) +__attribute__( ( always_inline ) ) static inline UINT __disable_interrupts(void) { -unsigned int primask_value; +UINT int_posture; - __asm__ volatile (" MRS %0,PRIMASK ": "=r" (primask_value) ); - return(primask_value); + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); } -__attribute__( ( always_inline ) ) static inline void __enable_interrupt(void) -{ - __asm__ volatile (" CPSIE i": : : "memory" ); -} - - __attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) { -unsigned int interrupt_save; +UINT interrupt_save; /* Set PendSV to invoke ThreadX scheduler. */ *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (__get_IPSR() == 0) + if (_tx_ipsr_get() == 0) { - interrupt_save = __get_primask_value(); - __enable_interrupt(); + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif __restore_interrupt(interrupt_save); } } - #define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; -#define TX_DISABLE interrupt_save = __disable_interrupt(); +#define TX_DISABLE interrupt_save = __disable_interrupts(); #define TX_RESTORE __restore_interrupt(interrupt_save); -#elif defined(__ICCARM__) /* IAR */ - -static void _tx_thread_system_return_inline(void) -{ -__istate_t interrupt_save; - - /* Set PendSV to invoke ThreadX scheduler. */ - *((ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); - if (__get_IPSR() == 0) - { - interrupt_save = __get_interrupt_state(); - __enable_interrupt(); - __set_interrupt_state(interrupt_save); - } -} - -#define TX_INTERRUPT_SAVE_AREA __istate_t interrupt_save; -#define TX_DISABLE {interrupt_save = __get_interrupt_state();__disable_interrupt();}; -#define TX_RESTORE {__set_interrupt_state(interrupt_save);}; - -#endif /* Interrupt disable/restore macros for each compiler. */ - /* Redefine _tx_thread_system_return for improved performance. */ - #define _tx_thread_system_return _tx_thread_system_return_inline - #else /* TX_DISABLE_INLINE is defined */ UINT _tx_thread_interrupt_disable(VOID); @@ -696,12 +688,11 @@ VOID _tx_thread_interrupt_restore(UIN #define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); #endif /* TX_DISABLE_INLINE */ - /* Define the version ID of ThreadX. This may be utilized by the application. */ #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Modules ARMv8-M Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M33 Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; @@ -710,5 +701,4 @@ extern CHAR _tx_version_id[]; #endif #endif - #endif diff --git a/ports_module/cortex_m33/iar/inc/tx_secure_interface.h b/ports_module/cortex_m33/iar/inc/tx_secure_interface.h index 0a2bdd67..d6fba106 100644 --- a/ports_module/cortex_m33/iar/inc/tx_secure_interface.h +++ b/ports_module/cortex_m33/iar/inc/tx_secure_interface.h @@ -51,9 +51,10 @@ /* Define internal secure thread stack function prototypes. */ -extern void _tx_thread_secure_stack_initialize(void); +extern UINT _tx_thread_secure_mode_stack_initialize(void); extern UINT _tx_thread_secure_mode_stack_allocate(TX_THREAD *thread_ptr, ULONG stack_size); extern UINT _tx_thread_secure_mode_stack_free(TX_THREAD *thread_ptr); +extern void _tx_thread_secure_stack_initialize(void); extern void _tx_thread_secure_stack_context_save(TX_THREAD *thread_ptr); extern void _tx_thread_secure_stack_context_restore(TX_THREAD *thread_ptr); diff --git a/ports_module/cortex_m33/iar/inc/txm_module_port.h b/ports_module/cortex_m33/iar/inc/txm_module_port.h index ae37777e..2f0f9a6b 100644 --- a/ports_module/cortex_m33/iar/inc/txm_module_port.h +++ b/ports_module/cortex_m33/iar/inc/txm_module_port.h @@ -25,8 +25,8 @@ /* */ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ -/* txm_module_port.h Cortex-M33/MPU/IAR */ -/* 6.1.5 */ +/* txm_module_port.h Cortex-M33/IAR */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 03-02-2021 Scott Larson Initial Version 6.1.5 */ +/* 01-31-2022 Scott Larson Modified comment(s), */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -350,6 +352,6 @@ ALIGN_TYPE _txm_module_manager_port_dispatch(TXM_MODULE_INSTANCE *module_instanc #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M33/MPU/IAR Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M33/IAR Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m4/ac5/example_build/build.bat b/ports_module/cortex_m4/ac5/example_build/build.bat deleted file mode 100644 index 57eae574..00000000 --- a/ports_module/cortex_m4/ac5/example_build/build.bat +++ /dev/null @@ -1,8 +0,0 @@ -@ECHO OFF -ECHO Starting build... -CALL build_threadx.bat -CALL build_threadx_demo.bat -CALL build_threadx_module_library.bat -CALL build_threadx_module_demo.bat -CALL build_threadx_module_manager_demo.bat -ECHO Build finished. diff --git a/ports_module/cortex_m4/ac5/example_build/clean.bat b/ports_module/cortex_m4/ac5/example_build/clean.bat deleted file mode 100644 index 3217f01b..00000000 --- a/ports_module/cortex_m4/ac5/example_build/clean.bat +++ /dev/null @@ -1,2 +0,0 @@ -@ECHO OFF -DEL *.o *.a *.axf *.map diff --git a/ports_module/cortex_m4/ac5/example_build/setenv.bat b/ports_module/cortex_m4/ac5/example_build/setenv.bat deleted file mode 100644 index 965f12f3..00000000 --- a/ports_module/cortex_m4/ac5/example_build/setenv.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off - -REM *** ARM DS 2020 -REM SET PATH=%ProgramFiles%\Arm\Development Studio 2020.0\sw\ARMCompiler5.06u6\bin;%PATH% -REM SET ARMLMD_LICENSE_FILE=%APPDATA%\arm\ds\licenses -REM SET ARM_CONFIG_PATH=%APPDATA%\arm\ds\2020.0 -REM SET ARM_PRODUCT_DEF=%ProgramFiles%\Arm\Development Studio 2020.0\sw\mappings\gold.elmap - -REM *** legacy ARM DS 5 -SET PATH=%ProgramFiles%\DS-5 v5.29.3\sw\ARMCompiler5.06u6\bin;%PATH% -SET ARMLMD_LICENSE_FILE=%APPDATA%\ARM\DS-5\licenses -SET ARM_CONFIG_PATH=%APPDATA%\ARM\DS-5_v5.29.3 -SET ARM_PRODUCT_PATH=%ProgramFiles%\DS-5 v5.29.3\sw\mappings diff --git a/ports_module/cortex_m4/ac5/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_m4/ac5/module_manager/src/tx_timer_interrupt.s index 7e694b07..0fc3a6c3 100644 --- a/ports_module/cortex_m4/ac5/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/cortex_m4/ac5/module_manager/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER EXPORT _tx_timer_interrupt _tx_timer_interrupt @@ -249,6 +253,7 @@ __tx_timer_nothing_expired DSB // Complete all memory access BX lr // Return to caller // } +#endif ALIGN LTORG END diff --git a/ports_module/cortex_m4/ac6/example_build/all.bat b/ports_module/cortex_m4/ac6/example_build/all.bat deleted file mode 100644 index 748e5743..00000000 --- a/ports_module/cortex_m4/ac6/example_build/all.bat +++ /dev/null @@ -1,5 +0,0 @@ -@ECHO OFF -CALL clean.bat -CALL setenv.bat -CALL initws.bat -CALL build.bat diff --git a/ports_module/cortex_m4/ac6/example_build/build.bat b/ports_module/cortex_m4/ac6/example_build/build.bat deleted file mode 100644 index 6ebdf460..00000000 --- a/ports_module/cortex_m4/ac6/example_build/build.bat +++ /dev/null @@ -1,24 +0,0 @@ -@ECHO OFF - -ECHO Build starting... - -SETLOCAL ENABLEEXTENSIONS - -IF DEFINED ARMDSIDEC GOTO IARBUILD_DEFINED -ECHO ERROR: please set ARMDSIDEC to the path of the ARM Developer Studio eclipsec.exe program -EXIT /B 2 -:IARBUILD_DEFINED - -IF EXIST %ARMDSIDEC% GOTO ARMDSIDEC_FOUND -ECHO ERROR: the command ARMDSIDEC doesn't exist: %ARMDSIDEC% -EXIT /B 2 -:ARMDSIDEC_FOUND - -%ARMDSIDEC% -nosplash -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data .\workspace -build all -IF %ERRORLEVEL% EQU 0 GOTO BUILD_OK -ECHO ERROR: build failed. -EXIT /B 1 -:BUILD_OK - -ECHO Build completed without errors. -EXIT /B 0 diff --git a/ports_module/cortex_m4/ac6/example_build/clean.bat b/ports_module/cortex_m4/ac6/example_build/clean.bat deleted file mode 100644 index 8a48e5c9..00000000 --- a/ports_module/cortex_m4/ac6/example_build/clean.bat +++ /dev/null @@ -1,4 +0,0 @@ -@ECHO OFF -ECHO Cleaning... -RMDIR /Q /S workspace -ECHO Done. diff --git a/ports_module/cortex_m4/ac6/example_build/initws.bat b/ports_module/cortex_m4/ac6/example_build/initws.bat deleted file mode 100644 index 62806594..00000000 --- a/ports_module/cortex_m4/ac6/example_build/initws.bat +++ /dev/null @@ -1,14 +0,0 @@ -@ECHO OFF - -ECHO Initializing the workspace... - -SETLOCAL ENABLEEXTENSIONS - -%ARMDSIDEC% -nosplash -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data .\workspace -import .\tx -import .\txm -import .\sample_threadx -import .\sample_threadx_module -import .\sample_threadx_module_manager -IF %ERRORLEVEL% EQU 0 GOTO WS_INITIALIZED -ECHO ERROR: failed to initialize the workspace -EXIT /B 2 - -:WS_INITIALIZED -echo Workspace initialized. -EXIT /B 0 diff --git a/ports_module/cortex_m4/ac6/example_build/setenv.bat b/ports_module/cortex_m4/ac6/example_build/setenv.bat deleted file mode 100644 index 27fecdfc..00000000 --- a/ports_module/cortex_m4/ac6/example_build/setenv.bat +++ /dev/null @@ -1,16 +0,0 @@ -@ECHO OFF - -SET ARMDSDIR="C:\Program Files\Arm\Development Studio 2020.0" -IF EXIST %ARMDSDIR% GOTO FOUND_ARMDS -ECHO ARM Development Studio not found. -EXIT /B 1 - -:FOUND_ARMDS -SET ARMDSIDEC=%ARMDSDIR%\bin\armds_idec.exe -IF EXIST %ARMDSIDEC% GOTO FOUND_ARMDS_IDEC -ECHO armds_idec.exe not found. -EXIT /B 1 - -:FOUND_ARMDS_IDEC -ECHO armds_idec.exe found at %ARMDSIDEC% -EXIT /B 0 diff --git a/ports_module/cortex_m4/ac6/inc/txm_module_port.h b/ports_module/cortex_m4/ac6/inc/txm_module_port.h index 3ffa5087..5542fe83 100644 --- a/ports_module/cortex_m4/ac6/inc/txm_module_port.h +++ b/ports_module/cortex_m4/ac6/inc/txm_module_port.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* txm_module_port.h Cortex-M4/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user-configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -91,6 +94,11 @@ The following extensions must also be defined in tx_port.h: VOID (*tx_timer_module_expiration_function)(ULONG id); */ +/* Users can define the module heap size. */ +#ifndef TXM_MODULE_HEAP_SIZE +#define TXM_MODULE_HEAP_SIZE 512 +#endif + /* Define the kernel stack size for a module thread. */ #ifndef TXM_MODULE_KERNEL_STACK_SIZE #define TXM_MODULE_KERNEL_STACK_SIZE 768 diff --git a/ports_module/cortex_m4/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_m4/ac6/module_lib/src/txm_module_initialize.S index 92291782..26e1bf4b 100644 --- a/ports_module/cortex_m4/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_m4/ac6/module_lib/src/txm_module_initialize.S @@ -33,7 +33,7 @@ /* FUNCTION RELEASE */ /* */ /* _txm_module_initialize Cortex-M4/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -63,14 +63,17 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _txm_module_initialize(VOID) .global _txm_module_initialize .thumb_func _txm_module_initialize: - PUSH {r4-r12,lr} // Save dregs and LR - //B __scatterload // Call ARM func to initialize variables + PUSH {r0-r12,lr} // Save dregs and LR + B __scatterload // Call ARM func to initialize variables // Override the __rt_exit function. .global __rt_exit @@ -79,25 +82,10 @@ __rt_exit: POP {r4-r12,lr} // Restore dregs and LR BX lr // Return to caller -#define TXM_MODULE_HEAP_SIZE 512 - -// returns heap start address in R0 -// returns heap end address in R2 -// does not touch SP, it is already set up before the module runs - .global __user_setup_stackheap - .thumb_func -__user_setup_stackheap: - LDR r1, _txm_heap // load heap offset - MOV r2, TXM_MODULE_HEAP_SIZE // load heap size - ADD r2, r2, r0 // calculate heap end address - BX lr - -// dummy main function - .global main - .thumb_func -main: - BX lr - - .align 8 -_txm_heap: - .zero TXM_MODULE_HEAP_SIZE + .global __rt_entry + .type __rt_entry, %function +__rt_entry: + POP {r0-r1} + BL __rt_lib_init + POP {r2-r12,lr} // Restore dregs and LR + BX lr // Return to caller diff --git a/ports_module/cortex_m4/ac6/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m4/ac6/module_lib/src/txm_module_thread_shell_entry.c index 2b191899..5359f19b 100644 --- a/ports_module/cortex_m4/ac6/module_lib/src/txm_module_thread_shell_entry.c +++ b/ports_module/cortex_m4/ac6/module_lib/src/txm_module_thread_shell_entry.c @@ -44,10 +44,12 @@ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param3); -/* Define the startup code that clears the uninitialized global data and sets up the - preset global variables. */ +/* Define the module's heap and align it to 8 bytes. */ +__attribute__((aligned(8))) UCHAR txm_heap[TXM_MODULE_HEAP_SIZE]; -extern VOID _txm_module_initialize(VOID); + +/* Use our asm routine that calls the ARM code to initialize data and heap. */ +extern VOID _txm_module_initialize(VOID *heap_base, VOID *heap_top); /**************************************************************************/ @@ -55,7 +57,7 @@ extern VOID _txm_module_initialize(VOID); /* FUNCTION RELEASE */ /* */ /* _txm_module_thread_shell_entry Cortex-M4/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -92,6 +94,9 @@ extern VOID _txm_module_initialize(VOID); /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) @@ -107,7 +112,7 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN if (thread_info -> txm_module_thread_entry_info_start_thread) { /* Initialize the C environment. */ - _txm_module_initialize(); + _txm_module_initialize(&txm_heap[0], &txm_heap[TXM_MODULE_HEAP_SIZE-1]); /* Save the entry info pointer, for later use. */ _txm_module_entry_info = thread_info; diff --git a/ports_module/cortex_m4/ac6/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m4/ac6/module_manager/src/tx_timer_interrupt.S index 6116f875..9385e1cb 100644 --- a/ports_module/cortex_m4/ac6/module_manager/src/tx_timer_interrupt.S +++ b/ports_module/cortex_m4/ac6/module_manager/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports_module/cortex_m4/gnu/example_build/build_all.bat b/ports_module/cortex_m4/gnu/example_build/build_all.bat deleted file mode 100644 index 70c43ec3..00000000 --- a/ports_module/cortex_m4/gnu/example_build/build_all.bat +++ /dev/null @@ -1,5 +0,0 @@ - -call build_threadx.bat -call build_threadx_module_library.bat -call build_threadx_module_sample.bat -call build_threadx_module_manager_sample.bat \ No newline at end of file diff --git a/ports_module/cortex_m4/gnu/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m4/gnu/module_manager/src/tx_thread_schedule.S index 217a8f60..1377997e 100644 --- a/ports_module/cortex_m4/gnu/module_manager/src/tx_thread_schedule.S +++ b/ports_module/cortex_m4/gnu/module_manager/src/tx_thread_schedule.S @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M4/GNU */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -74,6 +74,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -92,7 +94,7 @@ _tx_thread_schedule: LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag STR r0, [r2, #0] // Clear preempt disable flag -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit @@ -191,7 +193,7 @@ UsageFault_Handler: // Bit 7 = 1 -> MMFAR is valid STRB r1, [r0] // Clear the MMFSR -#ifdef __ARM_PCS_VFP +#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 @@ -258,7 +260,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -383,7 +385,7 @@ __tx_ts_restore: STR r1, [r0] // Enable MPU skip_mpu_setup: LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers @@ -448,14 +450,14 @@ __tx_SVCallHandler: #endif MRS r3, PSP // Pickup thread stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST lr, #0x10 // Test for extended module stack ITT EQ ORREQ r3, r3, #1 // If so, set LSB in thread stack pointer to indicate extended frame ORREQ lr, lr, #0x10 // Set bit, return with standard frame #endif STR r3, [r2, #0xB0] // Save thread stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP BIC r3, #1 // Clear possibly OR'd bit #endif @@ -504,7 +506,7 @@ _tx_thread_user_return: STR r3, [r2, #20] // Set stack size #endif -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* If lazy stacking is pending, check if it can be cleared. if(LSPACT && tx_thread_module_stack_start < FPCAR && FPCAR < tx_thread_module_stack_end) then clear LSPACT. */ @@ -528,7 +530,7 @@ _tx_no_lazy_clear: LDR r0, [r2, #0xB0] // Load the module thread stack pointer MRS r3, PSP // Pickup kernel stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST r0, #1 // Is module stack extended? ITTE NE // If so... BICNE lr, #0x10 // Clear bit, return with extended frame @@ -589,7 +591,7 @@ _txm_module_user_mode_exit: NOP // } -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP .global tx_thread_fpu_enable .thumb_func diff --git a/ports_module/cortex_m4/gnu/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m4/gnu/module_manager/src/tx_timer_interrupt.S index 810f959e..043921be 100644 --- a/ports_module/cortex_m4/gnu/module_manager/src/tx_timer_interrupt.S +++ b/ports_module/cortex_m4/gnu/module_manager/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports_module/cortex_m4/iar/inc/tx_port.h b/ports_module/cortex_m4/iar/inc/tx_port.h index 5ca644a6..2ade11b9 100644 --- a/ports_module/cortex_m4/iar/inc/tx_port.h +++ b/ports_module/cortex_m4/iar/inc/tx_port.h @@ -509,7 +509,7 @@ void tx_thread_fpu_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M4/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M4/IAR Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports_module/cortex_m4/iar/inc/txm_module_port.h b/ports_module/cortex_m4/iar/inc/txm_module_port.h index d9937791..d02fb1ad 100644 --- a/ports_module/cortex_m4/iar/inc/txm_module_port.h +++ b/ports_module/cortex_m4/iar/inc/txm_module_port.h @@ -377,6 +377,6 @@ UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M4/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M4/IAR Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m4/iar/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_m4/iar/module_manager/src/tx_timer_interrupt.s index f99d1755..1684e923 100644 --- a/ports_module/cortex_m4/iar/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/cortex_m4/iar/module_manager/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M4/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER PUBLIC _tx_timer_interrupt _tx_timer_interrupt: @@ -249,4 +253,5 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif END diff --git a/ports_module/cortex_m7/ac5/example_build/build.bat b/ports_module/cortex_m7/ac5/example_build/build.bat deleted file mode 100644 index 57eae574..00000000 --- a/ports_module/cortex_m7/ac5/example_build/build.bat +++ /dev/null @@ -1,8 +0,0 @@ -@ECHO OFF -ECHO Starting build... -CALL build_threadx.bat -CALL build_threadx_demo.bat -CALL build_threadx_module_library.bat -CALL build_threadx_module_demo.bat -CALL build_threadx_module_manager_demo.bat -ECHO Build finished. diff --git a/ports_module/cortex_m7/ac5/example_build/clean.bat b/ports_module/cortex_m7/ac5/example_build/clean.bat deleted file mode 100644 index 3217f01b..00000000 --- a/ports_module/cortex_m7/ac5/example_build/clean.bat +++ /dev/null @@ -1,2 +0,0 @@ -@ECHO OFF -DEL *.o *.a *.axf *.map diff --git a/ports_module/cortex_m7/ac5/example_build/setenv.bat b/ports_module/cortex_m7/ac5/example_build/setenv.bat deleted file mode 100644 index 965f12f3..00000000 --- a/ports_module/cortex_m7/ac5/example_build/setenv.bat +++ /dev/null @@ -1,13 +0,0 @@ -@echo off - -REM *** ARM DS 2020 -REM SET PATH=%ProgramFiles%\Arm\Development Studio 2020.0\sw\ARMCompiler5.06u6\bin;%PATH% -REM SET ARMLMD_LICENSE_FILE=%APPDATA%\arm\ds\licenses -REM SET ARM_CONFIG_PATH=%APPDATA%\arm\ds\2020.0 -REM SET ARM_PRODUCT_DEF=%ProgramFiles%\Arm\Development Studio 2020.0\sw\mappings\gold.elmap - -REM *** legacy ARM DS 5 -SET PATH=%ProgramFiles%\DS-5 v5.29.3\sw\ARMCompiler5.06u6\bin;%PATH% -SET ARMLMD_LICENSE_FILE=%APPDATA%\ARM\DS-5\licenses -SET ARM_CONFIG_PATH=%APPDATA%\ARM\DS-5_v5.29.3 -SET ARM_PRODUCT_PATH=%ProgramFiles%\DS-5 v5.29.3\sw\mappings diff --git a/ports_module/cortex_m7/ac5/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_m7/ac5/module_manager/src/tx_timer_interrupt.s index 38fec7e4..6c83c737 100644 --- a/ports_module/cortex_m7/ac5/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/cortex_m7/ac5/module_manager/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/AC5 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER EXPORT _tx_timer_interrupt _tx_timer_interrupt @@ -249,6 +253,7 @@ __tx_timer_nothing_expired DSB // Complete all memory access BX lr // Return to caller // } +#endif ALIGN LTORG END diff --git a/ports_module/cortex_m7/ac6/example_build/all.bat b/ports_module/cortex_m7/ac6/example_build/all.bat deleted file mode 100644 index 748e5743..00000000 --- a/ports_module/cortex_m7/ac6/example_build/all.bat +++ /dev/null @@ -1,5 +0,0 @@ -@ECHO OFF -CALL clean.bat -CALL setenv.bat -CALL initws.bat -CALL build.bat diff --git a/ports_module/cortex_m7/ac6/example_build/build.bat b/ports_module/cortex_m7/ac6/example_build/build.bat deleted file mode 100644 index 6ebdf460..00000000 --- a/ports_module/cortex_m7/ac6/example_build/build.bat +++ /dev/null @@ -1,24 +0,0 @@ -@ECHO OFF - -ECHO Build starting... - -SETLOCAL ENABLEEXTENSIONS - -IF DEFINED ARMDSIDEC GOTO IARBUILD_DEFINED -ECHO ERROR: please set ARMDSIDEC to the path of the ARM Developer Studio eclipsec.exe program -EXIT /B 2 -:IARBUILD_DEFINED - -IF EXIST %ARMDSIDEC% GOTO ARMDSIDEC_FOUND -ECHO ERROR: the command ARMDSIDEC doesn't exist: %ARMDSIDEC% -EXIT /B 2 -:ARMDSIDEC_FOUND - -%ARMDSIDEC% -nosplash -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data .\workspace -build all -IF %ERRORLEVEL% EQU 0 GOTO BUILD_OK -ECHO ERROR: build failed. -EXIT /B 1 -:BUILD_OK - -ECHO Build completed without errors. -EXIT /B 0 diff --git a/ports_module/cortex_m7/ac6/example_build/clean.bat b/ports_module/cortex_m7/ac6/example_build/clean.bat deleted file mode 100644 index 8a48e5c9..00000000 --- a/ports_module/cortex_m7/ac6/example_build/clean.bat +++ /dev/null @@ -1,4 +0,0 @@ -@ECHO OFF -ECHO Cleaning... -RMDIR /Q /S workspace -ECHO Done. diff --git a/ports_module/cortex_m7/ac6/example_build/initws.bat b/ports_module/cortex_m7/ac6/example_build/initws.bat deleted file mode 100644 index 62806594..00000000 --- a/ports_module/cortex_m7/ac6/example_build/initws.bat +++ /dev/null @@ -1,14 +0,0 @@ -@ECHO OFF - -ECHO Initializing the workspace... - -SETLOCAL ENABLEEXTENSIONS - -%ARMDSIDEC% -nosplash -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data .\workspace -import .\tx -import .\txm -import .\sample_threadx -import .\sample_threadx_module -import .\sample_threadx_module_manager -IF %ERRORLEVEL% EQU 0 GOTO WS_INITIALIZED -ECHO ERROR: failed to initialize the workspace -EXIT /B 2 - -:WS_INITIALIZED -echo Workspace initialized. -EXIT /B 0 diff --git a/ports_module/cortex_m7/ac6/example_build/setenv.bat b/ports_module/cortex_m7/ac6/example_build/setenv.bat deleted file mode 100644 index 27fecdfc..00000000 --- a/ports_module/cortex_m7/ac6/example_build/setenv.bat +++ /dev/null @@ -1,16 +0,0 @@ -@ECHO OFF - -SET ARMDSDIR="C:\Program Files\Arm\Development Studio 2020.0" -IF EXIST %ARMDSDIR% GOTO FOUND_ARMDS -ECHO ARM Development Studio not found. -EXIT /B 1 - -:FOUND_ARMDS -SET ARMDSIDEC=%ARMDSDIR%\bin\armds_idec.exe -IF EXIST %ARMDSIDEC% GOTO FOUND_ARMDS_IDEC -ECHO armds_idec.exe not found. -EXIT /B 1 - -:FOUND_ARMDS_IDEC -ECHO armds_idec.exe found at %ARMDSIDEC% -EXIT /B 0 diff --git a/ports_module/cortex_m7/ac6/inc/txm_module_port.h b/ports_module/cortex_m7/ac6/inc/txm_module_port.h index 1347c859..1fd6ce26 100644 --- a/ports_module/cortex_m7/ac6/inc/txm_module_port.h +++ b/ports_module/cortex_m7/ac6/inc/txm_module_port.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* txm_module_port.h Cortex-M7/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -41,6 +41,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user-configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -91,6 +94,11 @@ The following extensions must also be defined in tx_port.h: VOID (*tx_timer_module_expiration_function)(ULONG id); */ +/* Users can define the module heap size. */ +#ifndef TXM_MODULE_HEAP_SIZE +#define TXM_MODULE_HEAP_SIZE 512 +#endif + /* Define the kernel stack size for a module thread. */ #ifndef TXM_MODULE_KERNEL_STACK_SIZE #define TXM_MODULE_KERNEL_STACK_SIZE 768 diff --git a/ports_module/cortex_m7/ac6/module_lib/src/txm_module_initialize.S b/ports_module/cortex_m7/ac6/module_lib/src/txm_module_initialize.S index 23fba265..ccdc0ba9 100644 --- a/ports_module/cortex_m7/ac6/module_lib/src/txm_module_initialize.S +++ b/ports_module/cortex_m7/ac6/module_lib/src/txm_module_initialize.S @@ -33,7 +33,7 @@ /* FUNCTION RELEASE */ /* */ /* _txm_module_initialize Cortex-M7/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -63,14 +63,17 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _txm_module_initialize(VOID) .global _txm_module_initialize .thumb_func _txm_module_initialize: - PUSH {r4-r12,lr} // Save dregs and LR - //B __scatterload // Call ARM func to initialize variables + PUSH {r0-r12,lr} // Save dregs and LR + B __scatterload // Call ARM func to initialize variables // Override the __rt_exit function. .global __rt_exit @@ -79,25 +82,10 @@ __rt_exit: POP {r4-r12,lr} // Restore dregs and LR BX lr // Return to caller -#define TXM_MODULE_HEAP_SIZE 512 - -// returns heap start address in R0 -// returns heap end address in R2 -// does not touch SP, it is already set up before the module runs - .global __user_setup_stackheap - .thumb_func -__user_setup_stackheap: - LDR r1, _txm_heap // load heap offset - MOV r2, TXM_MODULE_HEAP_SIZE // load heap size - ADD r2, r2, r0 // calculate heap end address - BX lr - -// dummy main function - .global main - .thumb_func -main: - BX lr - - .align 8 -_txm_heap: - .zero TXM_MODULE_HEAP_SIZE + .global __rt_entry + .type __rt_entry, %function +__rt_entry: + POP {r0-r1} + BL __rt_lib_init + POP {r2-r12,lr} // Restore dregs and LR + BX lr // Return to caller diff --git a/ports_module/cortex_m7/ac6/module_lib/src/txm_module_thread_shell_entry.c b/ports_module/cortex_m7/ac6/module_lib/src/txm_module_thread_shell_entry.c index bb93a625..cf2cc166 100644 --- a/ports_module/cortex_m7/ac6/module_lib/src/txm_module_thread_shell_entry.c +++ b/ports_module/cortex_m7/ac6/module_lib/src/txm_module_thread_shell_entry.c @@ -44,10 +44,12 @@ TXM_MODULE_THREAD_ENTRY_INFO *_txm_module_entry_info; ULONG (*_txm_module_kernel_call_dispatcher)(ULONG kernel_request, ULONG param_1, ULONG param_2, ULONG param3); -/* Define the startup code that clears the uninitialized global data and sets up the - preset global variables. */ +/* Define the module's heap and align it to 8 bytes. */ +__attribute__((aligned(8))) UCHAR txm_heap[TXM_MODULE_HEAP_SIZE]; -extern VOID _txm_module_initialize(VOID); + +/* Use our asm routine that calls the ARM code to initialize data and heap. */ +extern VOID _txm_module_initialize(VOID *heap_base, VOID *heap_top); /**************************************************************************/ @@ -55,7 +57,7 @@ extern VOID _txm_module_initialize(VOID); /* FUNCTION RELEASE */ /* */ /* _txm_module_thread_shell_entry Cortex-M7/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -92,6 +94,9 @@ extern VOID _txm_module_initialize(VOID); /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Modified comments and made */ +/* heap user configurable, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_ENTRY_INFO *thread_info) @@ -107,7 +112,7 @@ VOID _txm_module_thread_shell_entry(TX_THREAD *thread_ptr, TXM_MODULE_THREAD_EN if (thread_info -> txm_module_thread_entry_info_start_thread) { /* Initialize the C environment. */ - _txm_module_initialize(); + _txm_module_initialize(&txm_heap[0], &txm_heap[TXM_MODULE_HEAP_SIZE-1]); /* Save the entry info pointer, for later use. */ _txm_module_entry_info = thread_info; diff --git a/ports_module/cortex_m7/ac6/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m7/ac6/module_manager/src/tx_timer_interrupt.S index c8ab5724..bde18d04 100644 --- a/ports_module/cortex_m7/ac6/module_manager/src/tx_timer_interrupt.S +++ b/ports_module/cortex_m7/ac6/module_manager/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/AC6 */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports_module/cortex_m7/gnu/example_build/build_all.bat b/ports_module/cortex_m7/gnu/example_build/build_all.bat deleted file mode 100644 index 70c43ec3..00000000 --- a/ports_module/cortex_m7/gnu/example_build/build_all.bat +++ /dev/null @@ -1,5 +0,0 @@ - -call build_threadx.bat -call build_threadx_module_library.bat -call build_threadx_module_sample.bat -call build_threadx_module_manager_sample.bat \ No newline at end of file diff --git a/ports_module/cortex_m7/gnu/module_manager/src/tx_thread_schedule.S b/ports_module/cortex_m7/gnu/module_manager/src/tx_thread_schedule.S index ccee84bb..2f472d6d 100644 --- a/ports_module/cortex_m7/gnu/module_manager/src/tx_thread_schedule.S +++ b/ports_module/cortex_m7/gnu/module_manager/src/tx_thread_schedule.S @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule Cortex-M7/GNU */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -74,6 +74,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 Scott Larson Initial Version 6.1.9 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -92,7 +94,7 @@ _tx_thread_schedule: LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag STR r0, [r2, #0] // Clear preempt disable flag -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ MRS r0, CONTROL // Pickup current CONTROL register BIC r0, r0, #4 // Clear the FPCA bit @@ -191,7 +193,7 @@ UsageFault_Handler: // Bit 7 = 1 -> MMFAR is valid STRB r1, [r0] // Clear the MMFSR -#ifdef __ARM_PCS_VFP +#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 @@ -258,7 +260,7 @@ __tx_ts_handler: STR r3, [r0] // Set _tx_thread_current_ptr to NULL MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) STMDB r12!, {r4-r11} // Save its remaining registers -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_save VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers @@ -383,7 +385,7 @@ __tx_ts_restore: STR r1, [r0] // Enable MPU skip_mpu_setup: LDMIA r12!, {LR} // Pickup LR -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST LR, #0x10 // Determine if the VFP extended frame is present BNE _skip_vfp_restore // If not, skip VFP restore VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers @@ -448,14 +450,14 @@ __tx_SVCallHandler: #endif MRS r3, PSP // Pickup thread stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST lr, #0x10 // Test for extended module stack ITT EQ ORREQ r3, r3, #1 // If so, set LSB in thread stack pointer to indicate extended frame ORREQ lr, lr, #0x10 // Set bit, return with standard frame #endif STR r3, [r2, #0xB0] // Save thread stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP BIC r3, #1 // Clear possibly OR'd bit #endif @@ -504,7 +506,7 @@ _tx_thread_user_return: STR r3, [r2, #20] // Set stack size #endif -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP /* If lazy stacking is pending, check if it can be cleared. if(LSPACT && tx_thread_module_stack_start < FPCAR && FPCAR < tx_thread_module_stack_end) then clear LSPACT. */ @@ -528,7 +530,7 @@ _tx_no_lazy_clear: LDR r0, [r2, #0xB0] // Load the module thread stack pointer MRS r3, PSP // Pickup kernel stack pointer -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP TST r0, #1 // Is module stack extended? ITTE NE // If so... BICNE lr, #0x10 // Clear bit, return with extended frame @@ -589,7 +591,7 @@ _txm_module_user_mode_exit: NOP // } -#ifdef __ARM_PCS_VFP +#ifdef __ARM_FP .global tx_thread_fpu_enable .thumb_func diff --git a/ports_module/cortex_m7/gnu/module_manager/src/tx_timer_interrupt.S b/ports_module/cortex_m7/gnu/module_manager/src/tx_timer_interrupt.S index 7cf4d4f3..d0fc6929 100644 --- a/ports_module/cortex_m7/gnu/module_manager/src/tx_timer_interrupt.S +++ b/ports_module/cortex_m7/gnu/module_manager/src/tx_timer_interrupt.S @@ -38,7 +38,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/GNU */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -71,11 +71,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER .global _tx_timer_interrupt .thumb_func _tx_timer_interrupt: @@ -248,3 +252,4 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif diff --git a/ports_module/cortex_m7/iar/inc/tx_port.h b/ports_module/cortex_m7/iar/inc/tx_port.h index 7e935920..4bcad5d2 100644 --- a/ports_module/cortex_m7/iar/inc/tx_port.h +++ b/ports_module/cortex_m7/iar/inc/tx_port.h @@ -509,7 +509,7 @@ void tx_thread_fpu_disable(void); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M7/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-M7/IAR Version 6.1.10 *"; #else #ifdef TX_MISRA_ENABLE extern CHAR _tx_version_id[100]; diff --git a/ports_module/cortex_m7/iar/inc/txm_module_port.h b/ports_module/cortex_m7/iar/inc/txm_module_port.h index 458a610a..153d6604 100644 --- a/ports_module/cortex_m7/iar/inc/txm_module_port.h +++ b/ports_module/cortex_m7/iar/inc/txm_module_port.h @@ -377,6 +377,6 @@ UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M7/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module Cortex-M7/IAR Version 6.1.10 *"; #endif diff --git a/ports_module/cortex_m7/iar/module_manager/src/tx_timer_interrupt.s b/ports_module/cortex_m7/iar/module_manager/src/tx_timer_interrupt.s index 7c899085..27396fd0 100644 --- a/ports_module/cortex_m7/iar/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/cortex_m7/iar/module_manager/src/tx_timer_interrupt.s @@ -40,7 +40,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_timer_interrupt Cortex-M7/IAR */ -/* 6.1.7 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -73,11 +73,15 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) // { +#ifndef TX_NO_TIMER PUBLIC _tx_timer_interrupt _tx_timer_interrupt: @@ -249,4 +253,5 @@ __tx_timer_nothing_expired: DSB // Complete all memory access BX lr // Return to caller // } +#endif END diff --git a/ports_module/rxv2/iar/example_build/tx_initialize_low_level.s b/ports_module/rxv2/iar/example_build/tx_initialize_low_level.s index d04b99b5..a378721b 100644 --- a/ports_module/rxv2/iar/example_build/tx_initialize_low_level.s +++ b/ports_module/rxv2/iar/example_build/tx_initialize_low_level.s @@ -31,7 +31,7 @@ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_initialize_low_level RXv2/IAR */ -;/* 6.x */ +;/* 6.1.10 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -64,7 +64,7 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 01-31-2022 William E. Lamie Initial Version 6.1.10 */ ;/* */ ;/**************************************************************************/ public __tx_initialize_low_level diff --git a/ports_module/rxv2/iar/inc/tx_port.h b/ports_module/rxv2/iar/inc/tx_port.h index c99acaea..e8cfabfd 100644 --- a/ports_module/rxv2/iar/inc/tx_port.h +++ b/ports_module/rxv2/iar/inc/tx_port.h @@ -26,7 +26,7 @@ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ /* tx_port.h RXv2/IAR */ -/* 6.x */ +/* 6.1.9 */ /* */ /* AUTHOR */ /* */ @@ -47,7 +47,11 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +/* 06-02-2021 William E. Lamie Modified comments, */ +/* resulting in version 6.1.7 */ +/* 10-15-2021 William E. Lamie Modified comment(s), */ +/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ @@ -282,7 +286,7 @@ extern volatile ULONG _tx_thread_system_state; #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX RXv2/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX RXv2/IAR Version 6.1.9 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports_module/rxv2/iar/inc/txm_module_port.h b/ports_module/rxv2/iar/inc/txm_module_port.h index 56c8c192..11274096 100644 --- a/ports_module/rxv2/iar/inc/txm_module_port.h +++ b/ports_module/rxv2/iar/inc/txm_module_port.h @@ -26,7 +26,7 @@ /* APPLICATION INTERFACE DEFINITION RELEASE */ /* */ /* txm_module_port.h RXv2/IAR */ -/* 6.x */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -40,7 +40,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* xx-xx-xxxx Scott Larson Initial Version 6.x */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ /* */ /**************************************************************************/ @@ -396,6 +396,6 @@ VOID _txm_module_manager_setup_mpu_registers(TXM_MODULE_INSTANCE *module_instan #define TXM_MODULE_MANAGER_VERSION_ID \ CHAR _txm_module_manager_version_id[] = \ - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module RXv2/IAR Version 6.x *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Module RXv2/IAR Version 6.1.10 *"; #endif diff --git a/ports_module/rxv2/iar/module_manager/src/tx_initialize_low_level.s b/ports_module/rxv2/iar/module_manager/src/tx_initialize_low_level.s index 612fae3a..933e14ad 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_initialize_low_level.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_initialize_low_level.s @@ -29,7 +29,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_initialize_low_level RXv2/IAR */ -/* 6.x */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -62,7 +62,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +/* 01-31-2022 William E. Lamie Initial Version 6.1.10 */ /* */ /**************************************************************************/ public __tx_initialize_low_level diff --git a/ports_module/rxv2/iar/module_manager/src/tx_thread_context_restore.s b/ports_module/rxv2/iar/module_manager/src/tx_thread_context_restore.s index 6ab87d64..f629f065 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_thread_context_restore.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_thread_context_restore.s @@ -19,7 +19,18 @@ ;/** */ ;/**************************************************************************/ ;/**************************************************************************/ - +; +; +;#define TX_SOURCE_CODE +; +; +;/* Include necessary system files. */ +; +;#include "tx_api.h" +;#include "tx_thread.h" +;#include "tx_timer.h" +; +; extern __tx_thread_system_state extern __tx_thread_current_ptr extern __tx_thread_preempt_disable @@ -34,7 +45,7 @@ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_thread_context_restore RXv2/IAR */ -;/* 6.x */ +;/* 6.1.9 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -66,7 +77,11 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +;/* 10-15-2021 William E. Lamie Modified comment(s), and */ +;/* removed unnecessary stack */ +;/* type placement, */ +;/* resulting in version 6.1.9 */ ;/* */ ;/**************************************************************************/ public __tx_thread_context_restore @@ -75,7 +90,7 @@ __tx_thread_context_restore: ; ; /* Lockout interrupts. */ - CLRPSW I ; disable interrupts + CLRPSW I ; Disable interrupts ; /* Determine if interrupts are nested. */ ; if (--_tx_thread_system_state) @@ -94,11 +109,11 @@ __tx_thread_context_restore: ; and return to the point of interrupt. */ ; __tx_thread_nested_restore: - POPC FPSW ; restore FPU status - POPM R14-R15 ; restore R14-R15 - POPM R3-R5 ; restore R3-R5 - POPM R1-R2 ; restore R1-R2 - RTE ; return to point of interrupt, restore PSW including IPL + POPC FPSW ; Restore FPU status + POPM R14-R15 ; Restore R14-R15 + POPM R3-R5 ; Restore R3-R5 + POPM R1-R2 ; Restore R1-R2 + RTE ; Return to point of interrupt, restore PSW including IPL ; } __tx_thread_not_nested_restore: @@ -113,22 +128,22 @@ __tx_thread_not_nested_restore: CMP #0, R2 BEQ __tx_thread_idle_system_restore - MOV.L #__tx_thread_preempt_disable, R3 ; pick up preempt disable flag + MOV.L #__tx_thread_preempt_disable, R3 ; Pick up preempt disable flag MOV.L [R3], R3 CMP #0, R3 - BNE __tx_thread_no_preempt_restore ; if pre-empt disable flag set, we simply return to the original point of interrupt regardless + BNE __tx_thread_no_preempt_restore ; If pre-empt disable flag set, we simply return to the original point of interrupt regardless MOV.L #__tx_thread_execute_ptr, R3 ; (_tx_thread_current_ptr != _tx_thread_execute_ptr) CMP [R3], R2 - BNE __tx_thread_preempt_restore ; jump to pre-empt restoring + BNE __tx_thread_preempt_restore ; Jump to pre-empt restoring ; __tx_thread_no_preempt_restore: - SETPSW U ; user stack - POPC FPSW ; restore FPU status - POPM R14-R15 ; restore R14-R15 - POPM R3-R5 ; restore R3-R5 - POPM R1-R2 ; restore R1-R2 - RTE ; return to point of interrupt, restore PSW including IPL + SETPSW U ; User stack + POPC FPSW ; Restore FPU status + POPM R14-R15 ; Restore R14-R15 + POPM R3-R5 ; Restore R3-R5 + POPM R1-R2 ; Restore R1-R2 + RTE ; Return to point of interrupt, restore PSW including IPL ; } ; else @@ -143,7 +158,7 @@ __tx_thread_preempt_restore: MOV.L #__tx_timer_time_slice, R3 ; Pickup time-slice address MOV.L [R3],R4 ; Pickup actual time-slice CMP #0, R4 - BEQ __tx_thread_dont_save_ts ; no time slice to save + BEQ __tx_thread_dont_save_ts ; No time slice to save ; ; _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; ; _tx_timer_time_slice = 0; @@ -156,7 +171,7 @@ __tx_thread_dont_save_ts: ; ; /* Now store the remaining registers! */ - SETPSW U ; user stack + SETPSW U ; User stack PUSHM R6-R13 MVFACGU #0, A1, R4 ; Save accumulators. @@ -168,9 +183,6 @@ __tx_thread_dont_save_ts: MVFACLO #0, A0, R6 PUSHM R4-R6 - MOV.L #1, R3 ; indicate interrupt stack frame - PUSH.L R3 - ; ; /* Clear the current task pointer. */ ; _tx_thread_current_ptr = TX_NULL; @@ -185,8 +197,8 @@ __tx_thread_dont_save_ts: ; _tx_thread_schedule(); __tx_thread_idle_system_restore: - MVTC #0, PSW ; reset interrupt priority level to 0 - BRA __tx_thread_schedule ; jump to scheduler + MVTC #0, PSW ; Reset interrupt priority level to 0 + BRA __tx_thread_schedule ; Jump to scheduler ; } ; ;} diff --git a/ports_module/rxv2/iar/module_manager/src/tx_thread_context_save.s b/ports_module/rxv2/iar/module_manager/src/tx_thread_context_save.s index 4ee01065..c1e10e4a 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_thread_context_save.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_thread_context_save.s @@ -19,7 +19,17 @@ ;/** */ ;/**************************************************************************/ ;/**************************************************************************/ - +; +; +;#define TX_SOURCE_CODE +; +; +;/* Include necessary system files. */ +; +;#include "tx_api.h" +;#include "tx_thread.h" +;#include "tx_timer.h" +; extern __tx_thread_system_state extern __tx_thread_current_ptr @@ -29,7 +39,7 @@ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_thread_context_save RXv2/IAR */ -;/* 6.x */ +;/* 6.1.9 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -60,7 +70,9 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +;/* 10-15-2021 William E. Lamie Modified comment(s), */ +;/* resulting in version 6.1.9 */ ;/* */ ;/**************************************************************************/ ;VOID _tx_thread_context_save(VOID) @@ -83,8 +95,8 @@ __tx_thread_context_save: ; { ; - MOV.L #__tx_thread_system_state, R1 ; pick up address of system state - MOV.L [R1], R2 ; pick up system state + MOV.L #__tx_thread_system_state, R1 ; Pick up address of system state + MOV.L [R1], R2 ; Pick up system state CMP #0, R2 ; 0 -> no nesting BEQ __tx_thread_not_nested_save ; @@ -96,11 +108,11 @@ __tx_thread_context_save: ; ; /* Save the rest of the scratch registers on the interrupt stack and return to the ; calling ISR. */ - POP R1 ; recuperate return address from stack + POP R1 ; Recuperate return address from stack PUSHM R3-R5 PUSHM R14-R15 PUSHC FPSW ; (top) FPSW, R14, R15, R3, R4, R5, R1, R2, PC, PSW (bottom) - JMP R1 ; return address was preserved in R1 + JMP R1 ; Return address was preserved in R1 ; __tx_thread_not_nested_save: @@ -121,27 +133,27 @@ __tx_thread_not_nested_save: ; /* Move stack frame over to the current threads stack. */ ; /* complete stack frame with registers not saved yet (R3-R5, R14-R15, FPSW) */ ; - MVFC USP, R1 ; pick up user stack pointer + MVFC USP, R1 ; Pick up user stack pointer MOV.L 16[R0], R2 - MOV.L R2, [-R1] ; save PSW on thread stack + MOV.L R2, [-R1] ; Save PSW on thread stack MOV.L 12[R0], R2 - MOV.L R2, [-R1] ; save PC on thread stack + MOV.L R2, [-R1] ; Save PC on thread stack MOV.L 8[R0], R2 - MOV.L R2, [-R1] ; save R2 on thread stack + MOV.L R2, [-R1] ; Save R2 on thread stack MOV.L 4[R0], R2 - MOV.L R2, [-R1] ; save R1 on thread stack - MOV.L R5, [-R1] ; save R5 on thread stack - MOV.L R4, [-R1] ; save R4 on thread stack - MOV.L R3, [-R1] ; save R3 on thread stack - MOV.L R15, [-R1] ; save R15 on thread stack - MOV.L R14, [-R1] ; save R14 on thread stack + MOV.L R2, [-R1] ; Save R1 on thread stack + MOV.L R5, [-R1] ; Save R5 on thread stack + MOV.L R4, [-R1] ; Save R4 on thread stack + MOV.L R3, [-R1] ; Save R3 on thread stack + MOV.L R15, [-R1] ; Save R15 on thread stack + MOV.L R14, [-R1] ; Save R14 on thread stack MVFC FPSW, R3 - MOV.L R3, [-R1] ; save FPSW on thread stack + MOV.L R3, [-R1] ; Save FPSW on thread stack - POP R2 ; pick up return address from interrupt stack - ADD #16, R0, R0 ; correct interrupt stack pointer back to the bottom - MVTC R1, USP ; set user/thread stack pointer - JMP R2 ; return to ISR + POP R2 ; Pick up return address from interrupt stack + ADD #16, R0, R0 ; Correct interrupt stack pointer back to the bottom + MVTC R1, USP ; Set user/thread stack pointer + JMP R2 ; Return to ISR ; } ; else @@ -151,9 +163,9 @@ __tx_thread_idle_system_save: ; ; /* Interrupt occurred in the scheduling loop. */ ; - POP R1 ; pick up return address - ADD #16, R0, R0 ; correct interrupt stack pointer back to the bottom (PC), don't care about saved registers - JMP R1 ; return to caller + POP R1 ; Pick up return address + ADD #16, R0, R0 ; Correct interrupt stack pointer back to the bottom (PC), don't care about saved registers + JMP R1 ; Return to caller ; ; } ;} diff --git a/ports_module/rxv2/iar/module_manager/src/tx_thread_interrupt_control.s b/ports_module/rxv2/iar/module_manager/src/tx_thread_interrupt_control.s index cfedd4b4..130fe418 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_thread_interrupt_control.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_thread_interrupt_control.s @@ -19,14 +19,22 @@ ;/** */ ;/**************************************************************************/ ;/**************************************************************************/ - +; +;#define TX_SOURCE_CODE +; +; +;/* Include necessary system files. */ +; +;#include "tx_api.h" +;#include "tx_thread.h" +; section .text:CODE:ROOT ;/**************************************************************************/ ;/* */ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_thread_interrupt_control RXv2/IAR */ -;/* 6.x */ +;/* 6.1.9 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -56,7 +64,9 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +;/* 10-15-2021 William E. Lamie Modified comment(s), */ +;/* resulting in version 6.1.9 */ ;/* */ ;/**************************************************************************/ ;UINT _tx_thread_interrupt_control(UINT new_posture) @@ -74,10 +84,10 @@ __tx_thread_interrupt_control: ; /* Apply the new interrupt posture. */ ; - BTST #16, R1 ; test I bit of PSW of "new posture" - BMNE #16, R2 ; conditionally set I bit of intermediate posture + BTST #16, R1 ; Test I bit of PSW of "new posture" + BMNE #16, R2 ; Conditionally set I bit of intermediate posture - MVTC R2, PSW ; save intermediate posture to PSW + MVTC R2, PSW ; Save intermediate posture to PSW MOV.L R3,R1 ; Get original SR RTS ; Return to caller diff --git a/ports_module/rxv2/iar/module_manager/src/tx_thread_schedule.s b/ports_module/rxv2/iar/module_manager/src/tx_thread_schedule.s index 2056d21b..55833f4a 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_thread_schedule.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_thread_schedule.s @@ -35,7 +35,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_schedule RXv2/IAR */ -/* 6.x */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -68,7 +68,11 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* xx-xx-xxxx Scott Larson Initial Version 6.x */ +/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +/* 10-15-2021 William E. Lamie Modified comment(s), and */ +/* removed unnecessary stack */ +/* type checking, */ +/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -94,7 +98,7 @@ __tx_thread_schedule_loop: /* Yes! We have a thread to execute. Lockout interrupts and transfer control to it. */ - CLRPSW I // disable interrupts + CLRPSW I // Disable interrupts /* Setup the current thread pointer. */ // _tx_thread_current_ptr = _tx_thread_execute_ptr; @@ -118,7 +122,7 @@ __tx_thread_schedule_loop: /* Switch to the thread's stack. */ // SP = _tx_thread_execute_ptr -> tx_thread_stack_ptr; - SETPSW U // user stack mode + SETPSW U // User stack mode MOV.L 8[R2],SP // Pickup stack pointer @@ -167,13 +171,6 @@ __tx_thread_schedule_loop: skip_mpu_setup - - /* Determine if an interrupt frame or a synchronous task suspension frame is present. */ - - POP R1 // Pickup stack type - CMP #1, R1 // Is it an interrupt stack? - BNE __tx_thread_synch_return // No, a synchronous return frame is present. - POPM R1-R3 // Restore accumulators. MVTACLO R3, A0 MVTACHI R2, A0 @@ -188,12 +185,8 @@ skip_mpu_setup POPM R14-R15 POPM R3-R5 POPM R1-R2 - RTE // return to point of interrupt, this restores PC and PSW + RTE // Return to point of interrupt, this restores PC and PSW -__tx_thread_synch_return - POPC PSW - POPM R6-R13 // Recover solicited stack frame - RTS // } extern __tx_thread_context_save diff --git a/ports_module/rxv2/iar/module_manager/src/tx_thread_stack_build.s b/ports_module/rxv2/iar/module_manager/src/tx_thread_stack_build.s index 393e3916..fce82aae 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_thread_stack_build.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_thread_stack_build.s @@ -28,7 +28,7 @@ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_thread_stack_build RXv2/IAR */ -;/* 6.x */ +;/* 6.1.9 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -60,20 +60,21 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +;/* 10-15-2021 William E. Lamie Modified comment(s), and */ +;/* removed unnecessary stack */ +;/* type placement, */ +;/* resulting in version 6.1.9 */ ;/* */ ;/**************************************************************************/ - public __tx_thread_stack_build - __tx_thread_stack_build: ; ; ; /* Build an interrupt frame. The form of the fake interrupt stack ; on the Renesas RX should look like the following after it is built: ; -; Stack Top: 1 Interrupt stack frame type -; ACC0 +; Stack Top: ACC0 ; ACC1 ; R6 ; R7 @@ -98,33 +99,33 @@ __tx_thread_stack_build: ; Stack Bottom: (higher memory address) */ ; MOV.L 16[R1],R3 ; Pickup end of stack area - BCLR #0, R3 ; mask for 4-byte alignment + BCLR #0, R3 ; Mask for 4-byte alignment BCLR #1, R3 ; ; /* Build the stack frame. */ ; MOV.L #30000h, R4 - MOV.L R4, [-R3] ; initial PSW (SVC mode, U flag set) - MOV.L R2, [-R3] ; initial PC + MOV.L R4, [-R3] ; Initial PSW (SVC mode, U flag set) + MOV.L R2, [-R3] ; Initial PC MOV.L #0, R4 - MOV.L R4,[-R3] ; initial R2 ... - MOV.L R4,[-R3] ; initial R1 ... - MOV.L R4,[-R3] ; initial R5 ... - MOV.L R4,[-R3] ; initial R4 ... - MOV.L R4,[-R3] ; initial R3 ... - MOV.L R4,[-R3] ; initial R15 ... - MOV.L R4,[-R3] ; initial R14 ... + MOV.L R4,[-R3] ; Initial R2 ... + MOV.L R4,[-R3] ; Initial R1 ... + MOV.L R4,[-R3] ; Initial R5 ... + MOV.L R4,[-R3] ; Initial R4 ... + MOV.L R4,[-R3] ; Initial R3 ... + MOV.L R4,[-R3] ; Initial R15 ... + MOV.L R4,[-R3] ; Initial R14 ... MVFC FPSW, r4 - MOV.L R4, [-R3] ; initial FPSW + MOV.L R4, [-R3] ; Initial FPSW MOV.L #0, R4 - MOV.L R4,[-R3] ; initial R13 ... - MOV.L R4,[-R3] ; initial R12 ... - MOV.L R4,[-R3] ; initial R11 ... - MOV.L R4,[-R3] ; initial R10 ... - MOV.L R4,[-R3] ; initial R9 ... - MOV.L R4,[-R3] ; initial R8 ... - MOV.L R4,[-R3] ; initial R7 ... - MOV.L R4,[-R3] ; initial R6 ... + MOV.L R4,[-R3] ; Initial R13 ... + MOV.L R4,[-R3] ; Initial R12 ... + MOV.L R4,[-R3] ; Initial R11 ... + MOV.L R4,[-R3] ; Initial R10 ... + MOV.L R4,[-R3] ; Initial R9 ... + MOV.L R4,[-R3] ; Initial R8 ... + MOV.L R4,[-R3] ; Initial R7 ... + MOV.L R4,[-R3] ; Initial R6 ... MOV.L R4,[-R3] ; Accumulator 1 MOV.L R4,[-R3] @@ -134,12 +135,10 @@ __tx_thread_stack_build: MOV.L R4,[-R3] MOV.L R4,[-R3] - MOV.L #1, R4 - MOV.L R4,[-R3] ; indicate interrupt stack frame ; /* Setup stack pointer. */ ; thread_ptr -> tx_thread_stack_ptr = R1; MOV.L R3, 8[R1] - ; store initial SP in thread control block + ; Store initial SP in thread control block RTS ;} diff --git a/ports_module/rxv2/iar/module_manager/src/tx_thread_system_return.s b/ports_module/rxv2/iar/module_manager/src/tx_thread_system_return.s index 91dd86f2..e0be2778 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_thread_system_return.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_thread_system_return.s @@ -27,7 +27,7 @@ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_thread_system_return RXv2/IAR */ -;/* 6.x */ +;/* 6.1.9 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -59,7 +59,9 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 12-31-2020 William E. Lamie Initial Version 6.1.3 */ +;/* 10-15-2021 William E. Lamie Modified comment(s), */ +;/* resulting in version 6.1.9 */ ;/* */ ;/**************************************************************************/ diff --git a/ports_module/rxv2/iar/module_manager/src/tx_timer_interrupt.s b/ports_module/rxv2/iar/module_manager/src/tx_timer_interrupt.s index 8fe142e6..ffd0d5ab 100644 --- a/ports_module/rxv2/iar/module_manager/src/tx_timer_interrupt.s +++ b/ports_module/rxv2/iar/module_manager/src/tx_timer_interrupt.s @@ -37,7 +37,7 @@ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_timer_interrupt RXv2/IAR */ -;/* 6.x */ +;/* 6.1.9 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ @@ -73,7 +73,9 @@ ;/* */ ;/* DATE NAME DESCRIPTION */ ;/* */ -;/* xx-xx-xxxx William E. Lamie Initial Version 6.x */ +;/* 12-30-2020 William E. Lamie Initial Version 6.1.3 */ +;/* 10-15-2021 William E. Lamie Modified comment(s), */ +;/* resulting in version 6.1.9 */ ;/* */ ;/**************************************************************************/ @@ -134,7 +136,7 @@ __tx_timer_no_time_slice: ; MOV.L #__tx_timer_current_ptr, R1 ; Pickup address of current timer ptr MOV.L [R1], R2 ; Pickup current pointer - MOV.L [R2+], R1 ; pickup timer list entry, _tx_timer_current_ptr++ + MOV.L [R2+], R1 ; Pickup timer list entry, _tx_timer_current_ptr++ CMP #0, R1 ; Is timer pointer NULL? BEQ __tx_timer_no_timer ; Yes, no timer has expired @@ -175,7 +177,7 @@ __tx_timer_no_timer: ; __tx_timer_skip_wrap: MOV.L #__tx_timer_current_ptr,R1 - MOV.L R2, [R1] ; store in updated pointer in _tx_timer_current_ptr + MOV.L R2, [R1] ; Store in updated pointer in _tx_timer_current_ptr __tx_timer_done: ; @@ -229,7 +231,7 @@ __tx_timer_nothing_expired: POPM R1-R5 POPM R14-R15 ; - RTS ; return to point of interrupt + RTS ; Return to point of interrupt ; ;} - END \ No newline at end of file + END diff --git a/ports_module/rxv2/iar/module_manager/src/txm_module_manager_thread_stack_build.s b/ports_module/rxv2/iar/module_manager/src/txm_module_manager_thread_stack_build.s index e7df51a1..67ec95a9 100644 --- a/ports_module/rxv2/iar/module_manager/src/txm_module_manager_thread_stack_build.s +++ b/ports_module/rxv2/iar/module_manager/src/txm_module_manager_thread_stack_build.s @@ -27,7 +27,7 @@ /* FUNCTION RELEASE */ /* */ /* _txm_module_manager_thread_stack_build RXv2/IAR */ -/* 6.x */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* Scott Larson, Microsoft Corporation */ @@ -59,7 +59,7 @@ /* */ /* DATE NAME DESCRIPTION */ /* */ -/* xx-xx-xxxx Scott Larson Initial Version 6.x */ +/* 01-31-2022 Scott Larson Initial Version 6.1.10 */ /* */ /**************************************************************************/ // VOID _txm_module_manager_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(TX_THREAD *, TXM_MODULE_INSTANCE *)) diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.lock b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.lock deleted file mode 100644 index e69de29b..00000000 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.log b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.log deleted file mode 100644 index bfda2511..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.log +++ /dev/null @@ -1,3457 +0,0 @@ -!SESSION 2015-09-28 16:00:20.788 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 -data C:\temp1663\tesp - -!ENTRY org.eclipse.cdt.core 1 0 2015-09-28 16:03:16.758 -!MESSAGE Indexed 'tx' (0 sources, 0 headers) in 0.003 sec: 0 declarations; 0 references; 0 unresolved inclusions; 0 syntax errors; 0 unresolved names (0%) - -!ENTRY org.eclipse.cdt.core 1 0 2015-09-28 16:08:56.869 -!MESSAGE Indexed 'sample_threadx' (0 sources, 0 headers) in 0 sec: 0 declarations; 0 references; 0 unresolved inclusions; 0 syntax errors; 0 unresolved names (0%) - -!ENTRY org.eclipse.debug.core 4 5012 2015-09-28 16:13:02.280 -!MESSAGE org.xml.sax.SAXParseException; lineNumber: 438; columnNumber: 1; Content is not allowed in trailing section. occurred while reading launch configuration file: C:\temp1663\tesp\.metadata\.plugins\org.eclipse.debug.core\.launches\sample_threadx Debug.launch. -!STACK 0 -org.xml.sax.SAXParseException; lineNumber: 438; columnNumber: 1; Content is not allowed in trailing section. - at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203) - at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.fatalError(ErrorHandlerWrapper.java:177) - at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:441) - at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:368) - at com.sun.org.apache.xerces.internal.impl.XMLScanner.reportFatalError(XMLScanner.java:1436) - at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$TrailingMiscDriver.next(XMLDocumentScannerImpl.java:1433) - at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:606) - at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:510) - at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:848) - at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:777) - at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) - at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:243) - at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:348) - at org.eclipse.debug.internal.core.LaunchManager.createInfoFromXML(LaunchManager.java:954) - at org.eclipse.debug.internal.core.LaunchManager.getInfo(LaunchManager.java:1372) - at org.eclipse.debug.internal.core.LaunchConfiguration.getInfo(LaunchConfiguration.java:470) - at org.eclipse.debug.internal.core.LaunchConfiguration.getAttribute(LaunchConfiguration.java:416) - at org.eclipse.debug.ui.RefreshTab.getRefreshScope(RefreshTab.java:431) - at org.eclipse.cdt.launch.AbstractCLaunchDelegate$CLaunch.refresh(AbstractCLaunchDelegate.java:109) - at org.eclipse.cdt.launch.internal.ui.LaunchUIPlugin.launchesTerminated(LaunchUIPlugin.java:225) - at org.eclipse.debug.internal.core.LaunchManager$LaunchesNotifier.run(LaunchManager.java:318) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.internal.core.LaunchManager$LaunchesNotifier.notify(LaunchManager.java:270) - at org.eclipse.debug.internal.core.LaunchManager.fireUpdate(LaunchManager.java:1055) - at org.eclipse.debug.core.Launch.fireTerminate(Launch.java:405) - at org.eclipse.debug.core.Launch.handleDebugEvents(Launch.java:577) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) - -!ENTRY org.eclipse.e4.ui.workbench.swt 4 2 2015-09-28 16:13:17.470 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.e4.ui.workbench.swt". -!STACK 0 -org.eclipse.e4.core.di.InjectionException: org.eclipse.swt.SWTException: Widget is disposed - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:62) - at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:888) - at org.eclipse.e4.core.internal.di.InjectorImpl.disposed(InjectorImpl.java:390) - at org.eclipse.e4.core.internal.di.Requestor.disposed(Requestor.java:143) - at org.eclipse.e4.core.internal.contexts.ContextObjectSupplier$ContextInjectionListener.update(ContextObjectSupplier.java:76) - at org.eclipse.e4.core.internal.contexts.TrackableComputationExt.update(TrackableComputationExt.java:107) - at org.eclipse.e4.core.internal.contexts.TrackableComputationExt.handleInvalid(TrackableComputationExt.java:70) - at org.eclipse.e4.core.internal.contexts.EclipseContext.dispose(EclipseContext.java:175) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.clearContext(PartRenderingEngine.java:974) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeRemoveGui(PartRenderingEngine.java:954) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$3(PartRenderingEngine.java:862) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$8.run(PartRenderingEngine.java:857) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.removeGui(PartRenderingEngine.java:841) - at org.eclipse.ui.internal.WorkbenchWindow.hardClose(WorkbenchWindow.java:1937) - at org.eclipse.ui.internal.WorkbenchWindow.busyClose(WorkbenchWindow.java:1560) - at org.eclipse.ui.internal.WorkbenchWindow.access$15(WorkbenchWindow.java:1527) - at org.eclipse.ui.internal.WorkbenchWindow$10.run(WorkbenchWindow.java:1592) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70) - at org.eclipse.ui.internal.WorkbenchWindow.close(WorkbenchWindow.java:1589) - at org.eclipse.ui.internal.Workbench$14.run(Workbench.java:1155) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench.busyClose(Workbench.java:1137) - at org.eclipse.ui.internal.Workbench.access$21(Workbench.java:1079) - at org.eclipse.ui.internal.Workbench$19.run(Workbench.java:1410) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70) - at org.eclipse.ui.internal.Workbench.close(Workbench.java:1407) - at org.eclipse.ui.internal.Workbench.restart(Workbench.java:2677) - at org.eclipse.ui.internal.ide.actions.OpenWorkspaceAction.restart(OpenWorkspaceAction.java:282) - at org.eclipse.ui.internal.ide.actions.OpenWorkspaceAction.run(OpenWorkspaceAction.java:264) - at org.eclipse.ui.internal.ide.actions.OpenWorkspaceAction$OpenDialogAction.run(OpenWorkspaceAction.java:70) - at org.eclipse.jface.action.Action.runWithEvent(Action.java:519) - at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:595) - at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:511) - at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:420) - at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84) - at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4353) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1061) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1085) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1070) - at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:782) - at org.eclipse.jface.action.ActionContributionItem$9.handleEvent(ActionContributionItem.java:1293) - at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84) - at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4353) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1061) - at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4172) - at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3761) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:1151) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1032) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:148) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:636) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:579) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:135) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603) - at org.eclipse.equinox.launcher.Main.run(Main.java:1465) -Caused by: org.eclipse.swt.SWTException: Widget is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Widget.error(Widget.java:476) - at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:348) - at org.eclipse.swt.widgets.Shell.getSize(Shell.java:1092) - at org.eclipse.ui.internal.quickaccess.SearchField.storeDialog(SearchField.java:580) - at org.eclipse.ui.internal.quickaccess.SearchField.dispose(SearchField.java:557) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:55) - ... 67 more - -!ENTRY org.eclipse.e4.ui.workbench 4 0 2015-09-28 16:13:17.470 -!MESSAGE Exception occurred while unrendering: org.eclipse.e4.ui.model.application.ui.basic.impl.TrimmedWindowImpl@74d228 (elementId: IDEWindow, tags: [topLevel], contributorURI: platform:/plugin/org.eclipse.ui.workbench) (widget: null, renderer: null, toBeRendered: true, onTop: false, visible: true, containerData: null, accessibilityPhrase: null) (label: %trimmedwindow.label.eclipseSDK, iconURI: null, tooltip: null, context: null, variables: [], x: 369, y: 52, width: 1024, height: 775) -!STACK 0 -org.eclipse.e4.core.di.InjectionException: org.eclipse.swt.SWTException: Widget is disposed - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:62) - at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:888) - at org.eclipse.e4.core.internal.di.InjectorImpl.disposed(InjectorImpl.java:390) - at org.eclipse.e4.core.internal.di.Requestor.disposed(Requestor.java:143) - at org.eclipse.e4.core.internal.contexts.ContextObjectSupplier$ContextInjectionListener.update(ContextObjectSupplier.java:76) - at org.eclipse.e4.core.internal.contexts.TrackableComputationExt.update(TrackableComputationExt.java:107) - at org.eclipse.e4.core.internal.contexts.TrackableComputationExt.handleInvalid(TrackableComputationExt.java:70) - at org.eclipse.e4.core.internal.contexts.EclipseContext.dispose(EclipseContext.java:175) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.clearContext(PartRenderingEngine.java:974) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeRemoveGui(PartRenderingEngine.java:954) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$3(PartRenderingEngine.java:862) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$8.run(PartRenderingEngine.java:857) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.removeGui(PartRenderingEngine.java:841) - at org.eclipse.ui.internal.WorkbenchWindow.hardClose(WorkbenchWindow.java:1937) - at org.eclipse.ui.internal.WorkbenchWindow.busyClose(WorkbenchWindow.java:1560) - at org.eclipse.ui.internal.WorkbenchWindow.access$15(WorkbenchWindow.java:1527) - at org.eclipse.ui.internal.WorkbenchWindow$10.run(WorkbenchWindow.java:1592) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70) - at org.eclipse.ui.internal.WorkbenchWindow.close(WorkbenchWindow.java:1589) - at org.eclipse.ui.internal.Workbench$14.run(Workbench.java:1155) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench.busyClose(Workbench.java:1137) - at org.eclipse.ui.internal.Workbench.access$21(Workbench.java:1079) - at org.eclipse.ui.internal.Workbench$19.run(Workbench.java:1410) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70) - at org.eclipse.ui.internal.Workbench.close(Workbench.java:1407) - at org.eclipse.ui.internal.Workbench.restart(Workbench.java:2677) - at org.eclipse.ui.internal.ide.actions.OpenWorkspaceAction.restart(OpenWorkspaceAction.java:282) - at org.eclipse.ui.internal.ide.actions.OpenWorkspaceAction.run(OpenWorkspaceAction.java:264) - at org.eclipse.ui.internal.ide.actions.OpenWorkspaceAction$OpenDialogAction.run(OpenWorkspaceAction.java:70) - at org.eclipse.jface.action.Action.runWithEvent(Action.java:519) - at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:595) - at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:511) - at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:420) - at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84) - at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4353) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1061) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1085) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1070) - at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:782) - at org.eclipse.jface.action.ActionContributionItem$9.handleEvent(ActionContributionItem.java:1293) - at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84) - at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4353) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1061) - at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4172) - at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3761) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:1151) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1032) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:148) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:636) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:579) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:135) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603) - at org.eclipse.equinox.launcher.Main.run(Main.java:1465) -Caused by: org.eclipse.swt.SWTException: Widget is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Widget.error(Widget.java:476) - at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:348) - at org.eclipse.swt.widgets.Shell.getSize(Shell.java:1092) - at org.eclipse.ui.internal.quickaccess.SearchField.storeDialog(SearchField.java:580) - at org.eclipse.ui.internal.quickaccess.SearchField.dispose(SearchField.java:557) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:55) - ... 67 more -!SESSION 2015-10-02 16:29:55.871 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1b69ccb, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1b69ccb, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@9f0360, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@9f0360, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@112b637, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@112b637, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ea0b48, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ea0b48, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1eb8e9a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1eb8e9a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15d1204, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15d1204, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@184a296, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@184a296, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1ea4b65, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1ea4b65, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-02 16:30:38.664 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@f6839c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@f6839c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) - -!ENTRY org.eclipse.debug.core 4 2 2015-10-02 17:42:00.298 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.debug.core". -!STACK 0 -org.eclipse.swt.SWTException: Device is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Display.error(Display.java:1258) - at org.eclipse.swt.widgets.Display.getThread(Display.java:2602) - at org.eclipse.debug.internal.ui.stringsubstitution.SelectedResourceManager.getActiveWindow(SelectedResourceManager.java:239) - at org.eclipse.debug.ui.DebugUITools.getDebugContext(DebugUITools.java:229) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.update(RemoveAllGlobalsActionDelegate.java:99) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.handleDebugEvents(RemoveAllGlobalsActionDelegate.java:131) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) - -!ENTRY org.eclipse.debug.core 4 125 2015-10-02 17:42:00.298 -!MESSAGE An exception occurred while dispatching debug events. -!STACK 0 -org.eclipse.swt.SWTException: Device is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Display.error(Display.java:1258) - at org.eclipse.swt.widgets.Display.getThread(Display.java:2602) - at org.eclipse.debug.internal.ui.stringsubstitution.SelectedResourceManager.getActiveWindow(SelectedResourceManager.java:239) - at org.eclipse.debug.ui.DebugUITools.getDebugContext(DebugUITools.java:229) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.update(RemoveAllGlobalsActionDelegate.java:99) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.handleDebugEvents(RemoveAllGlobalsActionDelegate.java:131) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) -!SESSION 2015-10-05 13:04:20.828 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3cdce6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3cdce6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15453dc, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15453dc, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@a8e1f7, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@a8e1f7, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@7edea3, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@7edea3, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ffee26, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ffee26, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1149b96, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1149b96, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@7c6dda, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@7c6dda, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3d31b0, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3d31b0, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-05 13:23:37.148 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@12a690f, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@12a690f, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2015-10-06 09:33:14.510 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@8206aa, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@8206aa, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@e8535e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@e8535e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1f4742b, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1f4742b, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1b3de87, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1b3de87, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@132284d, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@132284d, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15ae856, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15ae856, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15b83fa, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15b83fa, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3b3ab0, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3b3ab0, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-06 09:48:22.677 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@55167a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@55167a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2015-10-08 14:32:52.274 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 -data C:\temp1663\working_base_hs - -!ENTRY org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d85a18, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d85a18, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1575bad, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1575bad, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1e89472, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1e89472, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1965d0c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1965d0c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@182d1bc, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@182d1bc, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@8d7592, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@8d7592, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@19737f5, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@19737f5, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@aa73f6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@aa73f6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-08 14:58:59.494 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15e422e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@15e422e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) - -!ENTRY org.eclipse.cdt.debug.core 4 1000 2015-10-08 15:24:49.427 -!MESSAGE Internal error logged from CDI Debug: -!STACK 0 -org.eclipse.cdt.debug.core.cdi.TargetInvocationException: Can't load "C:\temp1663\working_base_hs\sample_threadx\Debug\sample_threadx.elf"[] - at com.arc.cdt.debug.seecode.internal.core.cdi.Target.loadProgram(Target.java:1289) - at com.arc.cdt.debug.seecode.internal.core.cdi.Target.confirmLoaded(Target.java:945) - at com.arc.cdt.debug.seecode.internal.core.cdi.Target.getRegisterGroups(Target.java:1816) - at org.eclipse.cdt.debug.internal.core.CRegisterManager.initialize(CRegisterManager.java:138) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.getRegisterManager(CDebugTarget.java:1808) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.initializeRegisters(CDebugTarget.java:425) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.initialize(CDebugTarget.java:319) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.(CDebugTarget.java:301) - at org.eclipse.cdt.debug.core.CDIDebugModel$1.run(CDIDebugModel.java:133) - at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2313) - at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2295) - at org.eclipse.cdt.debug.core.CDIDebugModel.newDebugTarget(CDIDebugModel.java:138) - at org.eclipse.cdt.launch.internal.LocalCDILaunchDelegate.launchLocalDebugSession(LocalCDILaunchDelegate.java:213) - at org.eclipse.cdt.launch.internal.LocalCDILaunchDelegate.launchDebugger(LocalCDILaunchDelegate.java:136) - at org.eclipse.cdt.launch.internal.LocalCDILaunchDelegate.launch(LocalCDILaunchDelegate.java:80) - at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:885) - at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:739) - at org.eclipse.debug.internal.ui.DebugUIPlugin.buildAndLaunch(DebugUIPlugin.java:1039) - at org.eclipse.debug.internal.ui.DebugUIPlugin$8.run(DebugUIPlugin.java:1256) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) - -!ENTRY org.eclipse.cdt.debug.core 4 1000 2015-10-08 15:25:33.059 -!MESSAGE Internal error logged from CDI Debug: -!STACK 0 -org.eclipse.cdt.debug.core.cdi.TargetInvocationException: Can't load "C:\temp1663\working_base_hs\sample_threadx\Debug\sample_threadx.elf"[] - at com.arc.cdt.debug.seecode.internal.core.cdi.Target.loadProgram(Target.java:1289) - at com.arc.cdt.debug.seecode.internal.core.cdi.Target.confirmLoaded(Target.java:945) - at com.arc.cdt.debug.seecode.internal.core.cdi.Target.getRegisterGroups(Target.java:1816) - at org.eclipse.cdt.debug.internal.core.CRegisterManager.initialize(CRegisterManager.java:138) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.getRegisterManager(CDebugTarget.java:1808) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.initializeRegisters(CDebugTarget.java:425) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.initialize(CDebugTarget.java:319) - at org.eclipse.cdt.debug.internal.core.model.CDebugTarget.(CDebugTarget.java:301) - at org.eclipse.cdt.debug.core.CDIDebugModel$1.run(CDIDebugModel.java:133) - at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2313) - at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:2295) - at org.eclipse.cdt.debug.core.CDIDebugModel.newDebugTarget(CDIDebugModel.java:138) - at org.eclipse.cdt.launch.internal.LocalCDILaunchDelegate.launchLocalDebugSession(LocalCDILaunchDelegate.java:213) - at org.eclipse.cdt.launch.internal.LocalCDILaunchDelegate.launchDebugger(LocalCDILaunchDelegate.java:136) - at org.eclipse.cdt.launch.internal.LocalCDILaunchDelegate.launch(LocalCDILaunchDelegate.java:80) - at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:885) - at org.eclipse.debug.internal.core.LaunchConfiguration.launch(LaunchConfiguration.java:739) - at org.eclipse.debug.internal.ui.DebugUIPlugin.buildAndLaunch(DebugUIPlugin.java:1039) - at org.eclipse.debug.internal.ui.DebugUIPlugin$8.run(DebugUIPlugin.java:1256) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) -!SESSION 2015-10-09 15:41:22.142 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.debug.ui 4 2 2015-10-09 16:32:33.850 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.debug.ui". -!STACK 0 -java.lang.NullPointerException - at org.eclipse.debug.core.sourcelookup.containers.CompositeSourceContainer.getSourceContainers(CompositeSourceContainer.java:134) - at org.eclipse.cdt.debug.internal.core.sourcelookup.SourceUtils.getCompilationPath(SourceUtils.java:205) - at org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector.getCompilationPath(CSourceLookupDirector.java:181) - at org.eclipse.cdt.debug.internal.ui.actions.RunToLineAdapter.convertPath(RunToLineAdapter.java:171) - at org.eclipse.cdt.debug.internal.ui.actions.RunToLineAdapter.canRunToLine(RunToLineAdapter.java:141) - at org.eclipse.debug.internal.ui.actions.RetargetRunToLineAction.canPerformAction(RetargetRunToLineAction.java:95) - at org.eclipse.debug.internal.ui.actions.RetargetAction.isTargetEnabled(RetargetAction.java:245) - at org.eclipse.debug.internal.ui.actions.RetargetRunToLineAction$DebugContextListener.contextActivated(RetargetRunToLineAction.java:51) - at org.eclipse.debug.internal.ui.actions.RetargetRunToLineAction$DebugContextListener.debugContextChanged(RetargetRunToLineAction.java:57) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService$1.run(DebugWindowContextService.java:223) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService.notify(DebugWindowContextService.java:220) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService.notify(DebugWindowContextService.java:195) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService.debugContextChanged(DebugWindowContextService.java:436) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider$1.run(AbstractDebugContextProvider.java:83) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider.fire(AbstractDebugContextProvider.java:80) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$ContextProviderProxy.debugContextChanged(LaunchView.java:518) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider$1.run(AbstractDebugContextProvider.java:83) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider.fire(AbstractDebugContextProvider.java:80) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$TreeViewerContextProvider.possibleChange(LaunchView.java:404) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$TreeViewerContextProvider$Visitor.visit(LaunchView.java:326) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.doAccept(ModelDelta.java:401) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.doAccept(ModelDelta.java:404) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.doAccept(ModelDelta.java:404) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.accept(ModelDelta.java:397) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$TreeViewerContextProvider.modelChanged(LaunchView.java:434) - at org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider.doModelChanged(TreeModelContentProvider.java:427) - at org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider.access$0(TreeModelContentProvider.java:413) - at org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider$2.run(TreeModelContentProvider.java:401) - at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35) - at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:136) - at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4147) - at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3764) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:1151) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1032) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:148) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:636) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:579) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:135) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603) - at org.eclipse.equinox.launcher.Main.run(Main.java:1465) - -!ENTRY org.eclipse.debug.ui 4 120 2015-10-09 16:32:33.881 -!MESSAGE Error logged from Debug UI: -!STACK 0 -java.lang.NullPointerException - at org.eclipse.debug.core.sourcelookup.containers.CompositeSourceContainer.getSourceContainers(CompositeSourceContainer.java:134) - at org.eclipse.cdt.debug.internal.core.sourcelookup.SourceUtils.getCompilationPath(SourceUtils.java:205) - at org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector.getCompilationPath(CSourceLookupDirector.java:181) - at org.eclipse.cdt.debug.internal.ui.actions.RunToLineAdapter.convertPath(RunToLineAdapter.java:171) - at org.eclipse.cdt.debug.internal.ui.actions.RunToLineAdapter.canRunToLine(RunToLineAdapter.java:141) - at org.eclipse.debug.internal.ui.actions.RetargetRunToLineAction.canPerformAction(RetargetRunToLineAction.java:95) - at org.eclipse.debug.internal.ui.actions.RetargetAction.isTargetEnabled(RetargetAction.java:245) - at org.eclipse.debug.internal.ui.actions.RetargetRunToLineAction$DebugContextListener.contextActivated(RetargetRunToLineAction.java:51) - at org.eclipse.debug.internal.ui.actions.RetargetRunToLineAction$DebugContextListener.debugContextChanged(RetargetRunToLineAction.java:57) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService$1.run(DebugWindowContextService.java:223) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService.notify(DebugWindowContextService.java:220) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService.notify(DebugWindowContextService.java:195) - at org.eclipse.debug.internal.ui.contexts.DebugWindowContextService.debugContextChanged(DebugWindowContextService.java:436) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider$1.run(AbstractDebugContextProvider.java:83) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider.fire(AbstractDebugContextProvider.java:80) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$ContextProviderProxy.debugContextChanged(LaunchView.java:518) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider$1.run(AbstractDebugContextProvider.java:83) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.ui.contexts.AbstractDebugContextProvider.fire(AbstractDebugContextProvider.java:80) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$TreeViewerContextProvider.possibleChange(LaunchView.java:404) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$TreeViewerContextProvider$Visitor.visit(LaunchView.java:326) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.doAccept(ModelDelta.java:401) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.doAccept(ModelDelta.java:404) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.doAccept(ModelDelta.java:404) - at org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta.accept(ModelDelta.java:397) - at org.eclipse.debug.internal.ui.views.launch.LaunchView$TreeViewerContextProvider.modelChanged(LaunchView.java:434) - at org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider.doModelChanged(TreeModelContentProvider.java:427) - at org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider.access$0(TreeModelContentProvider.java:413) - at org.eclipse.debug.internal.ui.viewers.model.TreeModelContentProvider$2.run(TreeModelContentProvider.java:401) - at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35) - at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:136) - at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:4147) - at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3764) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$9.run(PartRenderingEngine.java:1151) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1032) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:148) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:636) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:579) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:135) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:380) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:235) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603) - at org.eclipse.equinox.launcher.Main.run(Main.java:1465) - -!ENTRY org.eclipse.debug.core 4 2 2015-10-09 17:50:18.411 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.debug.core". -!STACK 0 -org.eclipse.swt.SWTException: Device is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Display.error(Display.java:1258) - at org.eclipse.swt.widgets.Display.getThread(Display.java:2602) - at org.eclipse.debug.internal.ui.stringsubstitution.SelectedResourceManager.getActiveWindow(SelectedResourceManager.java:239) - at org.eclipse.debug.ui.DebugUITools.getDebugContext(DebugUITools.java:229) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.update(RemoveAllGlobalsActionDelegate.java:99) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.handleDebugEvents(RemoveAllGlobalsActionDelegate.java:131) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) - -!ENTRY org.eclipse.debug.core 4 125 2015-10-09 17:50:18.411 -!MESSAGE An exception occurred while dispatching debug events. -!STACK 0 -org.eclipse.swt.SWTException: Device is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Display.error(Display.java:1258) - at org.eclipse.swt.widgets.Display.getThread(Display.java:2602) - at org.eclipse.debug.internal.ui.stringsubstitution.SelectedResourceManager.getActiveWindow(SelectedResourceManager.java:239) - at org.eclipse.debug.ui.DebugUITools.getDebugContext(DebugUITools.java:229) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.update(RemoveAllGlobalsActionDelegate.java:99) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.handleDebugEvents(RemoveAllGlobalsActionDelegate.java:131) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) -!SESSION 2015-10-12 11:12:05.433 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1a045bd, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1a045bd, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@fada78, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@fada78, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@cc9674, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@cc9674, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@120387e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@120387e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d8100a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d8100a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d925b3, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d925b3, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d46f2a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d46f2a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@754906, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@754906, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 11:45:51.753 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@db5482, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@db5482, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) - -!ENTRY org.eclipse.debug.core 4 2 2015-10-12 13:31:22.583 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.debug.core". -!STACK 0 -org.eclipse.swt.SWTException: Device is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Display.error(Display.java:1258) - at org.eclipse.swt.widgets.Display.getThread(Display.java:2602) - at org.eclipse.debug.internal.ui.stringsubstitution.SelectedResourceManager.getActiveWindow(SelectedResourceManager.java:239) - at org.eclipse.debug.ui.DebugUITools.getDebugContext(DebugUITools.java:229) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.update(RemoveAllGlobalsActionDelegate.java:99) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.handleDebugEvents(RemoveAllGlobalsActionDelegate.java:131) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) - -!ENTRY org.eclipse.debug.core 4 125 2015-10-12 13:31:22.583 -!MESSAGE An exception occurred while dispatching debug events. -!STACK 0 -org.eclipse.swt.SWTException: Device is disposed - at org.eclipse.swt.SWT.error(SWT.java:4441) - at org.eclipse.swt.SWT.error(SWT.java:4356) - at org.eclipse.swt.SWT.error(SWT.java:4327) - at org.eclipse.swt.widgets.Display.error(Display.java:1258) - at org.eclipse.swt.widgets.Display.getThread(Display.java:2602) - at org.eclipse.debug.internal.ui.stringsubstitution.SelectedResourceManager.getActiveWindow(SelectedResourceManager.java:239) - at org.eclipse.debug.ui.DebugUITools.getDebugContext(DebugUITools.java:229) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.update(RemoveAllGlobalsActionDelegate.java:99) - at org.eclipse.cdt.debug.internal.ui.actions.RemoveAllGlobalsActionDelegate.handleDebugEvents(RemoveAllGlobalsActionDelegate.java:131) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.run(DebugPlugin.java:1151) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.debug.core.DebugPlugin$EventNotifier.dispatch(DebugPlugin.java:1187) - at org.eclipse.debug.core.DebugPlugin$EventDispatchJob.run(DebugPlugin.java:431) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:54) -!SESSION 2015-10-12 13:33:52.888 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@906078, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@906078, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1d5c7f6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1d5c7f6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@124ab90, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@124ab90, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ef1934, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ef1934, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@11522f1, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@11522f1, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@5eba52, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@5eba52, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@34f8e2, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@34f8e2, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@2a7429, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@2a7429, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2015-10-12 13:34:34.005 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@a8c837, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@a8c837, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2017-04-05 21:13:21.705 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 -data C:\arc_hs_smp - -!ENTRY org.eclipse.e4.ui.workbench 2 0 2017-04-05 21:13:25.522 -!MESSAGE Could not run processor -!STACK 0 -org.eclipse.e4.core.di.InjectionException: java.lang.NullPointerException - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:62) - at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:247) - at org.eclipse.e4.core.internal.di.InjectorImpl.invoke(InjectorImpl.java:225) - at org.eclipse.e4.core.contexts.ContextInjectionFactory.invoke(ContextInjectionFactory.java:107) - at org.eclipse.e4.ui.internal.workbench.ModelAssembler.runProcessor(ModelAssembler.java:259) - at org.eclipse.e4.ui.internal.workbench.ModelAssembler.runProcessors(ModelAssembler.java:221) - at org.eclipse.e4.ui.internal.workbench.ModelAssembler.processModel(ModelAssembler.java:85) - at org.eclipse.e4.ui.internal.workbench.ResourceHandler.loadMostRecentModel(ResourceHandler.java:265) - at org.eclipse.e4.ui.internal.workbench.swt.E4Application.loadApplicationModel(E4Application.java:435) - at org.eclipse.e4.ui.internal.workbench.swt.E4Application.createE4Workbench(E4Application.java:260) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:601) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:579) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:150) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:135) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:382) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:236) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:648) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:603) - at org.eclipse.equinox.launcher.Main.run(Main.java:1465) -Caused by: java.lang.NullPointerException - at org.eclipse.cdt.launchbar.ui.internal.LaunchBarInjector.injectLaunchBar(LaunchBarInjector.java:109) - at org.eclipse.cdt.launchbar.ui.internal.LaunchBarInjector.injectIntoAll(LaunchBarInjector.java:84) - at org.eclipse.cdt.launchbar.ui.internal.LaunchBarInjector.execute(LaunchBarInjector.java:46) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:483) - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:55) - ... 26 more -!SESSION 2017-04-12 14:48:09.151 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.core.resources 2 10035 2017-04-12 14:48:12.987 -!MESSAGE The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes. -!SESSION 2017-04-13 18:52:12.107 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2017-04-13 18:59:00.860 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.860 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1e23e94, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1e23e94, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.860 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1c78556, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1c78556, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.860 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1134e01, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1134e01, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.860 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d28106, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d28106, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.860 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3c1b56, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3c1b56, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.861 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1b8ce72, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1b8ce72, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.861 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@e4eb42, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@e4eb42, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.861 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@388769, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@388769, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-13 18:59:00.861 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@54cdd3, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@54cdd3, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2017-04-14 09:52:07.324 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d6769b, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@d6769b, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1e2377, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1e2377, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ccb9f0, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@ccb9f0, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@a2eca6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@a2eca6, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1ef3847, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1ef3847, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@33bb96, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@33bb96, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@19b8759, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@19b8759, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@8d8f50, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@8d8f50, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 10:30:59.237 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@30ab46, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@30ab46, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2017-04-14 15:41:40.975 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@98adb2, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@98adb2, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3d0ff8, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3d0ff8, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@b73a92, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@b73a92, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@13faf1d, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@13faf1d, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@17835, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@17835, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@7f3c2, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@7f3c2, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.858 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@14134ba, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@14134ba, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.859 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1063e08, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1063e08, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 15:42:42.859 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@fb240d, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@fb240d, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2017-04-14 16:25:38.323 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 -data C:\arc_hs_smp - -!ENTRY org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@10dc104, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@10dc104, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@110ee00, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@110ee00, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@6a5b35, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@6a5b35, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@656a25, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@656a25, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@143800c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@143800c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1fb044a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1fb044a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@12c5533, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@12c5533, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@453e71, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@453e71, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-14 16:43:54.200 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@14f8b15, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@14f8b15, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2017-04-17 11:28:10.366 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 - -!ENTRY org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE Keybinding conflicts occurred. They may interfere with normal accelerator operation. -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for F4: -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@32db14, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F4, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.type.hierarchy,Open Type Hierarchy, - Open a type hierarchy on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@32db14, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for ALT+SHIFT+R: -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@87903a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+SHIFT+R, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.text.rename.element,Rename - Refactoring , - Renames the selected element, - Category(org.eclipse.cdt.ui.category.refactoring,Refactor - C++,C/C++ Refactorings,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@87903a, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for CTRL+SHIFT+T: -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3a9ceb, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+T, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.opentype,Open Element, - Open an element in an Editor, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@3a9ceb, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for CTRL+SHIFT+G: -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1148f1, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.findrefs,References, - Searches for references to the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@1148f1, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for CTRL+G: -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@49df5c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+G, - ParameterizedCommand(Command(org.eclipse.cdt.ui.search.finddecl,Declaration, - Searches for declarations of the selected element in the workspace, - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@49df5c, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for ALT+CTRL+H: -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@989af, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.call.hierarchy,Open Call Hierarchy, - Opens the call hierarchy for the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@989af, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for CTRL+SHIFT+H: -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@be88c1, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(CTRL+SHIFT+H, - ParameterizedCommand(Command(org.eclipse.cdt.ui.navigate.open.type.in.hierarchy,Open Type in Hierarchy, - Open a type in the type hierarchy view, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@be88c1, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for F3: -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@540be7, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(F3, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.opendecl,Open Declaration, - Opens an editor on the selected element's declaration(s), - Category(org.eclipse.cdt.ui.category.source,C/C++ Source,C/C++ Source Actions,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@540be7, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SUBENTRY 1 org.eclipse.jface 2 0 2017-04-17 14:06:33.920 -!MESSAGE A conflict occurred for ALT+CTRL+I: -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@2d850e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cEditorScope,,,system) -Binding(ALT+CTRL+I, - ParameterizedCommand(Command(org.eclipse.cdt.ui.edit.open.include.browser,Open Include Browser, - Open an include browser on the selected element, - Category(org.eclipse.ui.category.navigate,Navigate,null,true), - org.eclipse.ui.internal.WorkbenchHandlerServiceHandler@2d850e, - ,,true),null), - org.eclipse.ui.defaultAcceleratorConfiguration, - org.eclipse.cdt.ui.cViewScope,,,system) -!SESSION 2017-04-17 16:44:11.304 ----------------------------------------------- -eclipse.buildId=unknown -java.version=1.8.0_25 -java.vendor=Oracle Corporation -BootLoader constants: OS=win32, ARCH=x86, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86 -data C:\arc_hs_smp\for_intel_04-17-17 - -!ENTRY org.eclipse.core.resources 2 10035 2017-04-17 16:44:13.326 -!MESSAGE The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes. -!SESSION 2020-06-18 12:56:44.148 ----------------------------------------------- -eclipse.buildId=unknown -java.fullversion=1.8.0_212-b03 -JRE 1.8.0 Windows 8 amd64-64-Bit Compressed References 20190417_339 (JIT enabled, AOT enabled) -OpenJ9 - bad1d4d06 -OMR - 4a4278e6 -JCL - 5590c4f818 based on jdk8u212-b03 -BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86_64 - -!ENTRY org.eclipse.core.resources 4 567 2020-06-18 12:56:55.661 -!MESSAGE Workspace restored, but some problems occurred. -!SUBENTRY 1 org.eclipse.core.resources 4 567 2020-06-18 12:56:55.661 -!MESSAGE Could not read metadata for 'demo_threadx'. -!STACK 1 -org.eclipse.core.internal.resources.ResourceException: The project description file (.project) for 'demo_threadx' is missing. This file contains important information about the project. The project will not function properly until this file is restored. - at org.eclipse.core.internal.localstore.FileSystemResourceManager.read(FileSystemResourceManager.java:907) - at org.eclipse.core.internal.resources.SaveManager.restoreMetaInfo(SaveManager.java:904) - at org.eclipse.core.internal.resources.SaveManager.restoreMetaInfo(SaveManager.java:884) - at org.eclipse.core.internal.resources.SaveManager.restore(SaveManager.java:735) - at org.eclipse.core.internal.resources.SaveManager.startup(SaveManager.java:1587) - at org.eclipse.core.internal.resources.Workspace.startup(Workspace.java:2399) - at org.eclipse.core.internal.resources.Workspace.open(Workspace.java:2156) - at org.eclipse.core.resources.ResourcesPlugin.start(ResourcesPlugin.java:464) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.sources.SingleSourcePackage.loadClass(SingleSourcePackage.java:36) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:419) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:139) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610) - at org.eclipse.equinox.launcher.Main.run(Main.java:1519) -!SUBENTRY 2 org.eclipse.core.resources 4 567 2020-06-18 12:56:55.663 -!MESSAGE The project description file (.project) for 'demo_threadx' is missing. This file contains important information about the project. The project will not function properly until this file is restored. - -!ENTRY org.eclipse.ui 4 4 2020-06-18 12:57:01.938 -!MESSAGE Unable to create part -!SUBENTRY 1 org.eclipse.core.filebuffers 4 0 2020-06-18 12:57:01.938 -!MESSAGE Cannot determine URI for '/sample_threadx/arc.c'. -!STACK 1 -org.eclipse.core.runtime.CoreException: Cannot determine URI for '/sample_threadx/arc.c'. - at org.eclipse.core.internal.filebuffers.ResourceFileBuffer.create(ResourceFileBuffer.java:239) - at org.eclipse.core.internal.filebuffers.TextFileBufferManager.connect(TextFileBufferManager.java:112) - at org.eclipse.ui.editors.text.TextFileDocumentProvider.createFileInfo(TextFileDocumentProvider.java:560) - at org.eclipse.cdt.internal.ui.editor.CDocumentProvider.createFileInfo(CDocumentProvider.java:786) - at org.eclipse.ui.editors.text.TextFileDocumentProvider.connect(TextFileDocumentProvider.java:478) - at org.eclipse.cdt.internal.ui.editor.CDocumentProvider.connect(CDocumentProvider.java:718) - at org.eclipse.ui.texteditor.AbstractTextEditor.doSetInput(AbstractTextEditor.java:4178) - at org.eclipse.ui.texteditor.StatusTextEditor.doSetInput(StatusTextEditor.java:229) - at org.eclipse.ui.texteditor.AbstractDecoratedTextEditor.doSetInput(AbstractDecoratedTextEditor.java:1466) - at org.eclipse.ui.editors.text.TextEditor.doSetInput(TextEditor.java:150) - at org.eclipse.cdt.internal.ui.editor.CEditor.internalDoSetInput(CEditor.java:1368) - at org.eclipse.cdt.internal.ui.editor.CEditor.doSetInput(CEditor.java:1333) - at org.eclipse.ui.texteditor.AbstractTextEditor$5.run(AbstractTextEditor.java:3154) - at org.eclipse.ui.internal.WorkbenchWindow.run(WorkbenchWindow.java:2126) - at org.eclipse.ui.texteditor.AbstractTextEditor.internalInit(AbstractTextEditor.java:3172) - at org.eclipse.ui.texteditor.AbstractTextEditor.init(AbstractTextEditor.java:3197) - at org.eclipse.ui.internal.EditorReference.initialize(EditorReference.java:362) - at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.create(CompatibilityPart.java:318) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:55) - at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:966) - at org.eclipse.e4.core.internal.di.InjectorImpl.processAnnotated(InjectorImpl.java:931) - at org.eclipse.e4.core.internal.di.InjectorImpl.inject(InjectorImpl.java:151) - at org.eclipse.e4.core.internal.di.InjectorImpl.internalMake(InjectorImpl.java:375) - at org.eclipse.e4.core.internal.di.InjectorImpl.make(InjectorImpl.java:294) - at org.eclipse.e4.core.contexts.ContextInjectionFactory.make(ContextInjectionFactory.java:162) - at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.createFromBundle(ReflectionContributionFactory.java:105) - at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.doCreate(ReflectionContributionFactory.java:74) - at org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory.create(ReflectionContributionFactory.java:56) - at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer.createWidget(ContributedPartRenderer.java:129) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createWidget(PartRenderingEngine.java:975) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:651) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer.showTab(StackRenderer.java:1324) - at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.postProcess(LazyStackRenderer.java:103) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:669) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$1.run(PartRenderingEngine.java:536) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:520) - at org.eclipse.e4.ui.workbench.renderers.swt.ElementReferenceRenderer.createWidget(ElementReferenceRenderer.java:70) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createWidget(PartRenderingEngine.java:975) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:651) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.workbench.renderers.swt.SashRenderer.processContents(SashRenderer.java:142) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.workbench.renderers.swt.SashRenderer.processContents(SashRenderer.java:142) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.workbench.renderers.swt.SashRenderer.processContents(SashRenderer.java:142) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveRenderer.processContents(PerspectiveRenderer.java:49) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveStackRenderer.showTab(PerspectiveStackRenderer.java:82) - at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer.postProcess(LazyStackRenderer.java:103) - at org.eclipse.e4.ui.workbench.renderers.swt.PerspectiveStackRenderer.postProcess(PerspectiveStackRenderer.java:63) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:669) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.workbench.renderers.swt.SashRenderer.processContents(SashRenderer.java:142) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.workbench.renderers.swt.SWTPartRenderer.processContents(SWTPartRenderer.java:70) - at org.eclipse.e4.ui.workbench.renderers.swt.WBWRenderer.processContents(WBWRenderer.java:725) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:665) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.safeCreateGui(PartRenderingEngine.java:757) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.access$0(PartRenderingEngine.java:728) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$2.run(PartRenderingEngine.java:722) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.createGui(PartRenderingEngine.java:706) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1059) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:693) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:610) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610) - at org.eclipse.equinox.launcher.Main.run(Main.java:1519) -!SUBENTRY 2 org.eclipse.core.filebuffers 4 0 2020-06-18 12:57:01.938 -!MESSAGE Cannot determine URI for '/sample_threadx/arc.c'. - -!ENTRY org.eclipse.osgi 4 0 2020-06-18 12:57:02.815 -!MESSAGE An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). -!STACK 0 -org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -Root exception: -java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) - -!ENTRY org.eclipse.ui.workbench 4 2 2020-06-18 12:57:02.834 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.ui.workbench". -!STACK 1 -org.eclipse.core.runtime.CoreException: Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.throwException(RegistryStrategyOSGI.java:194) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:176) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - ... 10 more -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -!SUBENTRY 1 org.eclipse.equinox.registry 4 1 2020-06-18 12:57:02.836 -!MESSAGE Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. -!STACK 0 -java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -!SUBENTRY 1 org.eclipse.equinox.registry 4 1 2020-06-18 12:57:02.837 -!MESSAGE Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. -!STACK 0 -java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more - -!ENTRY org.eclipse.ui 4 0 2020-06-18 12:57:02.853 -!MESSAGE Unable to execute early startup code for the org.eclipse.ui.IStartup extension contributed by the 'com.synopsys.cdt.cnn.tools.ui' plug-in. -!STACK 1 -org.eclipse.core.runtime.CoreException: Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.throwException(RegistryStrategyOSGI.java:194) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:176) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - ... 10 more -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -!SUBENTRY 1 org.eclipse.equinox.registry 4 1 2020-06-18 12:57:02.854 -!MESSAGE Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. -!STACK 0 -java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more - -!ENTRY org.eclipse.e4.ui.workbench 2 0 2020-06-18 12:57:03.228 -!MESSAGE Removing part descriptor with the 'org.eclipse.cdt.debug.ui.DisassemblyView' id and the 'Disassembly' description. Points to the invalid 'bundleclass://org.eclipse.ui.workbench/org.eclipse.ui.internal.e4.compatibility.CompatibilityView' class. - -!ENTRY org.eclipse.e4.ui.workbench 4 0 2020-06-18 13:07:50.324 -!MESSAGE Error setting focus to : org.eclipse.e4.ui.model.application.ui.basic.impl.PartImpl tx_port.h -!STACK 0 -org.eclipse.swt.SWTException: Widget is disposed - at org.eclipse.swt.SWT.error(SWT.java:4533) - at org.eclipse.swt.SWT.error(SWT.java:4448) - at org.eclipse.swt.SWT.error(SWT.java:4419) - at org.eclipse.swt.widgets.Widget.error(Widget.java:482) - at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:354) - at org.eclipse.swt.widgets.Control.setFocus(Control.java:3445) - at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:1122) - at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:1122) - at org.eclipse.ui.texteditor.StatusTextEditor.setFocus(StatusTextEditor.java:118) - at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.delegateSetFocus(CompatibilityPart.java:203) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:55) - at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:282) - at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:288) - at org.eclipse.e4.core.internal.di.InjectorImpl.invoke(InjectorImpl.java:259) - at org.eclipse.e4.core.contexts.ContextInjectionFactory.invoke(ContextInjectionFactory.java:107) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.focusGui(PartRenderingEngine.java:779) - at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer$2.setFocus(ContributedPartRenderer.java:102) - at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer$6.mouseUp(StackRenderer.java:1151) - at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:221) - at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84) - at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4418) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1079) - at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4236) - at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3824) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1121) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:693) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:610) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610) - at org.eclipse.equinox.launcher.Main.run(Main.java:1519) - -!ENTRY org.eclipse.e4.ui.workbench 4 0 2020-06-18 13:07:51.530 -!MESSAGE Error setting focus to : org.eclipse.e4.ui.model.application.ui.basic.impl.PartImpl tx_api.h -!STACK 0 -org.eclipse.swt.SWTException: Widget is disposed - at org.eclipse.swt.SWT.error(SWT.java:4533) - at org.eclipse.swt.SWT.error(SWT.java:4448) - at org.eclipse.swt.SWT.error(SWT.java:4419) - at org.eclipse.swt.widgets.Widget.error(Widget.java:482) - at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:354) - at org.eclipse.swt.widgets.Control.setFocus(Control.java:3445) - at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:1122) - at org.eclipse.swt.widgets.Composite.setFocus(Composite.java:1122) - at org.eclipse.ui.texteditor.StatusTextEditor.setFocus(StatusTextEditor.java:118) - at org.eclipse.ui.internal.e4.compatibility.CompatibilityPart.delegateSetFocus(CompatibilityPart.java:203) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.e4.core.internal.di.MethodRequestor.execute(MethodRequestor.java:55) - at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:282) - at org.eclipse.e4.core.internal.di.InjectorImpl.invokeUsingClass(InjectorImpl.java:288) - at org.eclipse.e4.core.internal.di.InjectorImpl.invoke(InjectorImpl.java:259) - at org.eclipse.e4.core.contexts.ContextInjectionFactory.invoke(ContextInjectionFactory.java:107) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.focusGui(PartRenderingEngine.java:779) - at org.eclipse.e4.ui.workbench.renderers.swt.ContributedPartRenderer$2.setFocus(ContributedPartRenderer.java:102) - at org.eclipse.swt.custom.CTabItem.setFocus(CTabItem.java:332) - at org.eclipse.swt.custom.CTabFolder.setFocus(CTabFolder.java:2611) - at org.eclipse.swt.widgets.Control.fixFocus(Control.java:1069) - at org.eclipse.swt.widgets.Control.setVisible(Control.java:3972) - at org.eclipse.swt.custom.CTabFolder.setSelection(CTabFolder.java:3155) - at org.eclipse.swt.custom.CTabFolder.setSelection(CTabFolder.java:3112) - at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer.showTab(StackRenderer.java:1336) - at org.eclipse.e4.ui.workbench.renderers.swt.LazyStackRenderer$1.handleEvent(LazyStackRenderer.java:72) - at org.eclipse.e4.ui.services.internal.events.UIEventHandler$1.run(UIEventHandler.java:40) - at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:233) - at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:145) - at org.eclipse.swt.widgets.Display.syncExec(Display.java:4821) - at org.eclipse.e4.ui.internal.workbench.swt.E4Application$1.syncExec(E4Application.java:211) - at org.eclipse.e4.ui.services.internal.events.UIEventHandler.handleEvent(UIEventHandler.java:36) - at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:201) - at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:197) - at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:1) - at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) - at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:148) - at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:135) - at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:78) - at org.eclipse.equinox.internal.event.EventComponent.sendEvent(EventComponent.java:39) - at org.eclipse.e4.ui.services.internal.events.EventBroker.send(EventBroker.java:94) - at org.eclipse.e4.ui.internal.workbench.UIEventPublisher.notifyChanged(UIEventPublisher.java:60) - at org.eclipse.emf.common.notify.impl.BasicNotifierImpl.eNotify(BasicNotifierImpl.java:374) - at org.eclipse.e4.ui.model.application.ui.impl.ElementContainerImpl.setSelectedElement(ElementContainerImpl.java:173) - at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.hidePart(PartServiceImpl.java:1331) - at org.eclipse.e4.ui.internal.workbench.PartServiceImpl.hidePart(PartServiceImpl.java:1284) - at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer.closePart(StackRenderer.java:1296) - at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer.access$2(StackRenderer.java:1278) - at org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer$7.close(StackRenderer.java:1163) - at org.eclipse.swt.custom.CTabFolder.onMouse(CTabFolder.java:1930) - at org.eclipse.swt.custom.CTabFolder$1.handleEvent(CTabFolder.java:338) - at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84) - at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4418) - at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1079) - at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:4236) - at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3824) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1121) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) - at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022) - at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150) - at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:693) - at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336) - at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:610) - at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:148) - at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:138) - at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134) - at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388) - at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673) - at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610) - at org.eclipse.equinox.launcher.Main.run(Main.java:1519) - -!ENTRY org.eclipse.cdt.core 1 0 2020-06-18 13:08:13.337 -!MESSAGE Indexed 'sample_threadx' (2 sources, 2 headers) in 0.454 sec: 110 declarations; 266 references; 1 unresolved inclusions; 0 syntax errors; 105 unresolved names (22%) -!SESSION 2020-06-18 13:15:56.207 ----------------------------------------------- -eclipse.buildId=unknown -java.fullversion=1.8.0_212-b03 -JRE 1.8.0 Windows 8 amd64-64-Bit Compressed References 20190417_339 (JIT enabled, AOT enabled) -OpenJ9 - bad1d4d06 -OMR - 4a4278e6 -JCL - 5590c4f818 based on jdk8u212-b03 -BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=en_US -Command-line arguments: -os win32 -ws win32 -arch x86_64 - -!ENTRY org.eclipse.osgi 4 0 2020-06-18 13:20:45.992 -!MESSAGE An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). -!STACK 0 -org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -Root exception: -java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) - -!ENTRY org.eclipse.ui.workbench 4 2 2020-06-18 13:20:46.023 -!MESSAGE Problems occurred when invoking code from plug-in: "org.eclipse.ui.workbench". -!STACK 1 -org.eclipse.core.runtime.CoreException: Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.throwException(RegistryStrategyOSGI.java:194) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:176) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - ... 10 more -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -!SUBENTRY 1 org.eclipse.equinox.registry 4 1 2020-06-18 13:20:46.023 -!MESSAGE Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. -!STACK 0 -java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -!SUBENTRY 1 org.eclipse.equinox.registry 4 1 2020-06-18 13:20:46.023 -!MESSAGE Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. -!STACK 0 -java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more - -!ENTRY org.eclipse.ui 4 0 2020-06-18 13:20:46.039 -!MESSAGE Unable to execute early startup code for the org.eclipse.ui.IStartup extension contributed by the 'com.synopsys.cdt.cnn.tools.ui' plug-in. -!STACK 1 -org.eclipse.core.runtime.CoreException: Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.throwException(RegistryStrategyOSGI.java:194) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:176) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - ... 10 more -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more -!SUBENTRY 1 org.eclipse.equinox.registry 4 1 2020-06-18 13:20:46.039 -!MESSAGE Plug-in com.synopsys.cdt.cnn.tools.ui was unable to load class com.synopsys.cdt.cnn.tools.ui.LoadedAtStartup. -!STACK 0 -java.lang.ClassNotFoundException: An error occurred while automatically activating bundle com.synopsys.cdt.cnn.tools.ui (25). - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:116) - at org.eclipse.osgi.internal.loader.classpath.ClasspathManager.findLocalClass(ClasspathManager.java:529) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.findLocalClass(ModuleClassLoader.java:325) - at org.eclipse.osgi.internal.loader.BundleLoader.findLocalClass(BundleLoader.java:345) - at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:423) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372) - at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364) - at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161) - at java.lang.ClassLoader.loadClass(ClassLoader.java:874) - at org.eclipse.osgi.internal.framework.EquinoxBundle.loadClass(EquinoxBundle.java:564) - at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:174) - at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:905) - at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243) - at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:55) - at org.eclipse.ui.internal.WorkbenchPlugin$1.run(WorkbenchPlugin.java:291) - at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:52) - at org.eclipse.ui.internal.WorkbenchPlugin.createExtension(WorkbenchPlugin.java:286) - at org.eclipse.ui.internal.EarlyStartupRunnable.run(EarlyStartupRunnable.java:53) - at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42) - at org.eclipse.ui.internal.Workbench$55.run(Workbench.java:2835) - at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) -Caused by: org.osgi.framework.BundleException: Exception in com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start() of bundle com.synopsys.cdt.cnn.tools.ui. - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:795) - at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:724) - at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:932) - at org.eclipse.osgi.internal.framework.EquinoxBundle$EquinoxModule.startWorker(EquinoxBundle.java:309) - at org.eclipse.osgi.container.Module.doStart(Module.java:581) - at org.eclipse.osgi.container.Module.start(Module.java:449) - at org.eclipse.osgi.framework.util.SecureAction.start(SecureAction.java:470) - at org.eclipse.osgi.internal.hooks.EclipseLazyStarter.postFindLocalClass(EclipseLazyStarter.java:107) - ... 20 more -Caused by: java.lang.NullPointerException - at org.eclipse.core.runtime.Path.(Path.java:228) - at org.eclipse.core.runtime.Path.(Path.java:186) - at com.synopsys.cdt.cnn.tools.ui.Netron.registerExt(Netron.java:12) - at com.synopsys.cdt.cnn.tools.ui.CNNToolsUIPlugin.start(CNNToolsUIPlugin.java:52) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:774) - at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1) - at java.security.AccessController.doPrivileged(AccessController.java:703) - at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:767) - ... 27 more - -!ENTRY org.eclipse.e4.ui.workbench 2 0 2020-06-18 13:20:46.358 -!MESSAGE Removing part descriptor with the 'org.eclipse.cdt.debug.ui.DisassemblyView' id and the 'Disassembly' description. Points to the invalid 'bundleclass://org.eclipse.ui.workbench/org.eclipse.ui.internal.e4.compatibility.CompatibilityView' class. diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.codan.ui/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.codan.ui/dialog_settings.xml deleted file mode 100644 index bad736ef..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.codan.ui/dialog_settings.xml +++ /dev/null @@ -1,4 +0,0 @@ - -

- -
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/.log b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/.log deleted file mode 100644 index 1d051b80..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/.log +++ /dev/null @@ -1,27 +0,0 @@ -*** SESSION Sep 28, 2015 16:00:26.42 ------------------------------------------- -*** SESSION Sep 28, 2015 16:24:47.48 ------------------------------------------- -*** SESSION Sep 28, 2015 16:43:36.06 ------------------------------------------- -*** SESSION Oct 01, 2015 14:52:43.41 ------------------------------------------- -*** SESSION Oct 01, 2015 16:50:35.31 ------------------------------------------- -*** SESSION Oct 02, 2015 16:30:04.53 ------------------------------------------- -*** SESSION Oct 05, 2015 13:04:34.94 ------------------------------------------- -*** SESSION Oct 05, 2015 17:02:39.29 ------------------------------------------- -*** SESSION Oct 06, 2015 09:33:29.71 ------------------------------------------- -*** SESSION Oct 08, 2015 14:32:58.71 ------------------------------------------- -*** SESSION Oct 09, 2015 15:42:00.42 ------------------------------------------- -*** SESSION Oct 12, 2015 11:13:19.78 ------------------------------------------- -*** SESSION Oct 12, 2015 13:34:17.27 ------------------------------------------- -*** SESSION Oct 12, 2015 13:59:21.03 ------------------------------------------- -*** SESSION Apr 05, 2017 21:13:28.98 ------------------------------------------- -*** SESSION Apr 05, 2017 21:23:06.09 ------------------------------------------- -*** SESSION Apr 11, 2017 16:39:35.66 ------------------------------------------- -*** SESSION Apr 11, 2017 17:00:38.48 ------------------------------------------- -*** SESSION Apr 11, 2017 20:13:06.07 ------------------------------------------- -*** SESSION Apr 12, 2017 14:48:16.90 ------------------------------------------- -*** SESSION Apr 13, 2017 18:52:33.10 ------------------------------------------- -*** SESSION Apr 13, 2017 19:20:40.79 ------------------------------------------- -*** SESSION Apr 14, 2017 09:54:41.39 ------------------------------------------- -*** SESSION Apr 14, 2017 15:41:48.94 ------------------------------------------- -*** SESSION Apr 14, 2017 16:25:44.12 ------------------------------------------- -*** SESSION Apr 17, 2017 16:44:17.22 ------------------------------------------- -*** SESSION Jun 18, 2020 12:57:01.25 ------------------------------------------- diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.1443481736829.pdom b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.1443481736829.pdom deleted file mode 100644 index fc72688a7a2eea75f92d30256d81024c74c25f69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 344064 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

!BRvaFLD+<>TT+n60G4=|_{Mz#lR6lQ$9FaCL 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⪙eS6kln646c7}vEsoK=Evj2f1h61FnfLtw!h+yj~4E_;lfN#fwuH~ zS6p2^yR^8bT<@<^|8vW$%Zumn)qfA~bJXv~Hu2&7$8n{Pz7w8wMcdzFB8(#(AA?X)+`8WNx%lEI) zp$~J$OEb>Tpo!_E_0#a(zW+r3c~g;PZ5qN5;h#EO?epFh%>QLu6Jy~kJ>x-%{+IUU zK7Uiveoy~P?JK?0tYWXqC+7^;n_pI3?}n<_=ka>;(}wQ_-|z8y^UH?QCRd8b>&;Ia zz8}1i#|K0M1eQv|uN3~9c)Z^HsnS&?F!j?S)Z?(ur_w}zAc$`ps!pC2}y zI#-$I>Fdo88=gX+;|-73n_o734)mEGuQxw!crWnI9v{{~`bpz4Vfs#M08fX$m#42c zKW_Xp!81HwZ+;zIrkupSr!yViw_g0i`H}HsN9MTcjc}%+PonmFbTDBR-?N8Q=RxaY)*Gm)hIzN%Hui!7$*_@klR{{^NFRI(Wt;BxV6}H*L zf9Z{N``49A88?olZ)s*Uzpb_@`;mefO`ZKS1ARARv!uKe?w?S6rNeFgBGGuOR#JzF z-@gQIG)jLBZ6@otXXHpKSJ<0=9*ozLX@6csAT@ksf(MoBL{1xO`4g~rN|NVC_N~&Z)AMuGDX-+Z z`5Pz?watrG^Q5fe^N*bW6I4FU|F_uJ*!NYmWq*2GkKk$A>43k-@~o{QnU%)gKJ0hK zk`Fwcyq)|qr~9eC+yu4^dw*Wk-w5wi z)%|QGdsu4$-tu>K@(QZ@GdKPY>Ay5epZ142vq@c0McY@jhPT*y8}e7~-bQ)%Ha|8t z+G1%;9<)3^ck)!ZhCXX8{oOV(c~blbM?0pi{D9AI>UThzH7)WwlwaJD98^)as2@Sor#Neq@rdL(QGVp0f3yx(WYbm>_TL_apFf{P z|Z#1vA92UWd{PR5W9?~)jT zapj4T}0v{8M#P)ly_^t<%rE>v?BM{lfGqKEmNz zpZop5cT{AmmoL*_U%B+D?kgF=_z;JeX*^9nN&mna{l+;Squi6vq)+_%Gv?BQssi$S zoYU|Bm~HE0tiG!KQAPV-RpfE{-}u+{2fD@B<_DCvs?6bfzQC1(Eez8aMz6F@47c&? zvP}*T_034e{K+GA!3^T5dqj2peRzJ4>6{&H)XIB?Sw`Xej;GdN*m{U2oSnug7oHl<}slI6Ba2h|&-x(?c2UQy!j(o_Sgn$2f$@;;|hqgzY7Z|n|?w#|~zV!9i^v4#_{Xs!# znky?co-`Jgf9Qv*!B&2)pUvXG!{KJ%5mx0YNq#9T zUuVyl{ddK(kiDc}0y_ErnDKEhzt~smg31k&T(0(2t=qp)^(pf&=O<{T;~ygXRoAEJ znp4)F>NDuM7r(bf`ps~DQ@ng7e&E%0SzTP`d@`G(o2y+IUDrhOKh#fMFA~;&T^pi0 zpzj0m`oAxM2UWbI5Bg4v_P3~iNB^kfRkcw1yzljc?Oz@Nj(lPd21$4GUL!36!0c`be^Tp*0-pRS|ok$_Tw+Q_o{ReI<3Od_sq2R z!_w!=SHqLc_V%!kA-M_tq~C3i-i%%eJUAsoQfIpJb3$CEC14gu|6bBY+Wu4Y(?^N* zkBk5fKi&PEo7)}#roUqQ6M7%iKZHJx*Xv)_r>{T0qUkgJ%2WG4)ITk&AnR*0=}rE% z{?Ok_)GzI8Rqc@EeLjCpe=fHCxAw7;wytEH9YN;kRlg!XT0XRYW-FB}kyhINnSMYw z6wh&)L;W^?e6#v(^0gKEarSTeVLb9h`<>7aR=(@p{D964=p1TG?vGNAnm&tPk>jiD z!v^K>zd%wqhWbt8SLSimAH2_4CaK@X(^nD0d7n|_?t+2dwkYabLjtX$9kOGG@reiQp5>~qacAspmX?ThgK6fM6Q3pKUq$F}atXVUGV z{Rm#U3O~MlXDmXW)k@MWKK)8hc_(mtU#R^pXP;XBrMdJi>E!r7@TRoBz|Y^9d{C~5 z{_%3&_nL_r$KF1>`{-(7M)agRrfX*>BExBU(fE$y zCFsr5j^IgpFI&U>xW|>#EZu0;Bqw>&jQ{ zi>ge*S!_ozCGx*AEFaYmyrUyhS|A@5KJCejtvY^{kx@@Qu}}zX3;)|3UQazaT=Q=G z|DfN-c+4veN-+h3i<_Z4=8r{#~f-!mw;E|l|h%28dWUpnkB;>PG?cBnNBqHq>gL!)uQ9GiJy^Ym`tf44 zeqcV|A9#=R=XSgMyWZ%`9Nn$b<}`nQ*SN0BtDta@_J7X9#j*i$L+nkx3nyXsh(c%XBf-8)dhcY;TNLgZ=Wc$PxAJw-v3Zd+zf~R z*)2Io=>wVhf#*M#^6kg?p7x*S zKP8E|{|Su8wv%=@`~I8Za~y8{Kk|C~c4>RR*WVo;xBKec{dMQDzlt)-$&@Zj{rCWQ zP`!Y059*NKAE0+-UcOv9f8p;BFnpoIRla8}VlVavyTZ=&oASRNov-wIe*ehR;I~B1 zzEIrx{YJkyfd|!#DHz1?ERU1FmS>hfJ^3!2?3jW6)~b2NV3`>6M?NG5Z)dPijU&#<6yDj#}3k=|LP;TJ9^Ve+bi8}1%E%h&(9EBosOyHRS2_H|e>=Y~azEx?8P!So8@@V0uYQB-b%tC1 z8NSBh14;X*4(53D{L}OIQGdzHjAoJk1P`h=f}bRTT*nx~o8-^xX#7U|XM|zyM%K?X zeuLN8_^#1!qRzK>epY+|eSxlAaQSce7WikNhreMAnR>|K#!W-~OkP$lB#z}yzb!(3 zc8%2agRXvn2i4mguH&n<_}lcPB)9SMulG|`uXOdBat^9%k+1Kf`f2zMUqA78(S6|D zPZWl4_)cHH@%J|4o9$*r^jiO>V&fe}y+uCtE=alIL&^Nu_m5Tnr{=i%v*Y?pkY7UQ zCAPj!%^Zmi)z0{SB(I0Tl1@JI03@zrjuDCta-kIWsz#neFb6Qv33- z)~uDw(ep!`eXe-*UWaS@T2zXErX=#ce2n>yH3>`5LAN>i-m#f}cD5w_=-UUytM@w` z`k)#a*|t%V!t=isho7TIcE6#Xe78fAw)pV5Un`i|+2SnM{oOtJOkVBBFZKSIGum1E z9n2(ujv7h+vbj!u9;oI1ti^nnL6Qyi}QPw*QY`HFP7%kMuiJk8-MKSN)%`^30{!r8xiKiEdr{>>!swEo}o zHft1rBgwCX`J?HBr{auX%Lc~FK4>P%f3fjv9bcR`f8Lz(S@U#!ah@J!J*yNEHRsc3 z?|Ob@`d01${UfsSO1<>=B7e{gs|z^eg8I$

^}8x8 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 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.language.settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.language.settings.xml deleted file mode 100644 index 8d6acf2e..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.language.settings.xml +++ /dev/null @@ -1,6515 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c deleted file mode 100644 index 8b137891..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.c +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp deleted file mode 100644 index 8b137891..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.make.core/specs.cpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.managedbuilder.core/spec.c b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.managedbuilder.core/spec.c deleted file mode 100644 index e69de29b..00000000 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.managedbuilder.core/spec.cpp b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.managedbuilder.core/spec.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/cHelpSettings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/cHelpSettings.xml deleted file mode 100644 index 64868cbf..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/cHelpSettings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/dialog_settings.xml deleted file mode 100644 index 661aa8c7..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.ui/dialog_settings.xml +++ /dev/null @@ -1,11 +0,0 @@ - -

-
- - -
-
-
-
-
-
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.history/f0/60943445c62300171ed8c82249418230 b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.history/f0/60943445c62300171ed8c82249418230 deleted file mode 100644 index ca0ad3cd..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.history/f0/60943445c62300171ed8c82249418230 +++ /dev/null @@ -1,444 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) 1996-2017 by Express Logic Inc. */ -/* */ -/* This software is copyrighted by and is the sole property of Express */ -/* Logic, Inc. All rights, title, ownership, or other interests */ -/* in the software remain the property of Express Logic, Inc. This */ -/* software may only be used in accordance with the corresponding */ -/* license agreement. Any unauthorized use, duplication, transmission, */ -/* distribution, or disclosure of this software is expressly forbidden. */ -/* */ -/* This Copyright notice may not be removed or modified without prior */ -/* written consent of Express Logic, Inc. */ -/* */ -/* Express Logic, Inc. reserves the right to modify this software */ -/* without notice. */ -/* */ -/* Express Logic, Inc. info@expresslogic.com */ -/* 11423 West Bernardo Court http://www.expresslogic.com */ -/* San Diego, CA 92127 */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Port Specific */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - - -/**************************************************************************/ -/* */ -/* PORT SPECIFIC C INFORMATION RELEASE */ -/* */ -/* tx_port.h SMP/ARC_HS/MetaWare */ -/* 5.0 */ -/* */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Express Logic, Inc. */ -/* */ -/* DESCRIPTION */ -/* */ -/* This file contains data type definitions that make the ThreadX */ -/* real-time kernel function identically on a variety of different */ -/* processor architectures. For example, the size or number of bits */ -/* in an "int" data type vary between microprocessor architectures and */ -/* even C compilers for the same microprocessor. ThreadX does not */ -/* directly use native C data types. Instead, ThreadX creates its */ -/* own special types that can be mapped to actual data types by this */ -/* file to guarantee consistency in the interface and functionality. */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* xx-xx-2017 William E. Lamie Initial SMP/ARC HS/MetaWare */ -/* Support Version 5.0 */ -/* */ -/**************************************************************************/ - -#ifndef TX_PORT_H -#define TX_PORT_H - - -/* Remove volatile for ThreadX source on the ARC. This is because the ARC - compiler generates different non-cache r/w access when using volatile - that is different from the assembly language access of the same - global variables in ThreadX. */ - -#ifdef TX_SOURCE_CODE -#define volatile -#else -#ifdef NX_SOURCE_CODE -#define volatile -#else -#ifdef FX_SOURCE_CODE -#define volatile -#else -#ifdef UX_SOURCE_CODE -#define volatile -#endif -#endif -#endif -#endif - -/************* Define ThreadX SMP constants. *************/ - -/* Define the ThreadX SMP maximum number of cores. */ - -#ifndef TX_THREAD_SMP_MAX_CORES -#define TX_THREAD_SMP_MAX_CORES 2 -#endif - - -/* Define the ThreadX SMP core mask. */ - -#ifndef TX_THREAD_SMP_CORE_MASK -#define TX_THREAD_SMP_CORE_MASK 0x3 /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ -#endif - - -/* Define INLINE_DECLARE to whitespace for MetaWare compiler. */ - -#define INLINE_DECLARE - - -/* Define dynamic number of cores option. When commented out, the number of cores is static. */ - -/* #define TX_THREAD_SMP_DYNAMIC_CORE_MAX */ - - -/* Define ThreadX SMP initialization macro. */ - -#define TX_PORT_SPECIFIC_PRE_INITIALIZATION - - -/* Define ThreadX SMP pre-scheduler initialization. */ - -#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION - - -/* Enable the inter-core interrupt logic. */ - -#define TX_THREAD_SMP_INTER_CORE_INTERRUPT - - -/* Determine if there is customer-specific wakeup logic needed. */ - -#ifdef TX_THREAD_SMP_WAKEUP_LOGIC - -/* Include customer-specific wakeup code. */ - -#include "tx_thread_smp_core_wakeup.h" -#else - -#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC - -/* Default wakeup code. */ -#define TX_THREAD_SMP_WAKEUP_LOGIC -#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) -#endif -#endif - - -/* Ensure that the in-line resume/suspend define is not allowed. */ - -#ifdef TX_INLINE_THREAD_RESUME_SUSPEND -#undef TX_INLINE_THREAD_RESUME_SUSPEND -#endif - -/************* End ThreadX SMP constants. *************/ - - -/* Determine if the optional ThreadX user define file should be used. */ - -#ifdef TX_INCLUDE_USER_DEFINE_FILE - - -/* Yes, include the user defines in tx_user.h. The defines in this file may - alternately be defined on the command line. */ - -#include "tx_user.h" -#endif - - -/* Define compiler library include files. */ - -#include -#include - - -/* Define ThreadX basic types for this port. */ - -#define VOID void -typedef char CHAR; -typedef unsigned char UCHAR; -typedef int INT; -typedef unsigned int UINT; -typedef long LONG; -typedef unsigned long ULONG; -typedef short SHORT; -typedef unsigned short USHORT; - - -/* Define the priority levels for ThreadX. Legal values range - from 32 to 1024 and MUST be evenly divisible by 32. */ - -#ifndef TX_MAX_PRIORITIES -#define TX_MAX_PRIORITIES 32 -#endif - - -/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during - thread creation is less than this value, the thread create call will return an error. */ - -#ifndef TX_MINIMUM_STACK -#define TX_MINIMUM_STACK 800 /* Minimum stack size for this port */ -#endif - - -/* Define the system timer thread's default stack size and priority. These are only applicable - if TX_TIMER_PROCESS_IN_ISR is not defined. */ - -#ifndef TX_TIMER_THREAD_STACK_SIZE -#define TX_TIMER_THREAD_STACK_SIZE 2048 /* Default timer thread stack size */ -#endif - -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ -#endif - - -/* Define various constants for the ThreadX ARC HS port. */ - -#define TX_INT_ENABLE 0x0000001F /* Enable all interrupts */ -#define TX_INT_DISABLE_MASK 0x00000000 /* Disable all interrupts */ - - -/* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock - source constants would be: - -#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) -#define TX_TRACE_TIME_MASK 0x0000FFFFUL - -*/ - -#ifndef TX_TRACE_TIME_SOURCE -#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time -#endif -#ifndef TX_TRACE_TIME_MASK -#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL -#endif - - -/* Define the port specific options for the _tx_build_options variable. This variable indicates - how the ThreadX library was built. */ - -#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 - - -/* Define the in-line initialization constant so that modules with in-line - initialization capabilities can prevent their initialization from being - a function call. */ - -#define TX_INLINE_INITIALIZATION - - -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is - disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack - checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING - define is negated, thereby forcing the stack fill which is necessary for the stack checking - logic. */ - -#ifdef TX_ENABLE_STACK_CHECKING -#undef TX_DISABLE_STACK_FILLING -#endif - - -/* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with - existing ThreadX kernel awareness modules. */ - -#define TX_THREAD_EXTENSION_0 VOID *__mw_threadx_tls; \ - int __mw_errnum; \ - VOID (*__mw_thread_exit)(struct TX_THREAD_STRUCT *); -#define TX_THREAD_EXTENSION_1 -#define TX_THREAD_EXTENSION_2 -#define TX_THREAD_EXTENSION_3 - - -/* Define the port extensions of the remaining ThreadX objects. */ - -#define TX_BLOCK_POOL_EXTENSION -#define TX_BYTE_POOL_EXTENSION -#define TX_EVENT_FLAGS_GROUP_EXTENSION -#define TX_MUTEX_EXTENSION -#define TX_QUEUE_EXTENSION -#define TX_SEMAPHORE_EXTENSION -#define TX_TIMER_EXTENSION - - -/* Define the user extension field of the thread control block. Nothing - additional is needed for this port so it is defined as white space. */ - -#ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION -#endif - - -/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, - tx_thread_shell_entry, and tx_thread_terminate. */ - -#if __HIGHC__ - -/* The MetaWare thread safe C/C++ runtime library needs space to - store thread specific information. In addition, a function pointer - is also supplied so that certain thread-specific resources may be - released upon thread termination and/or thread completion. */ - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) \ - thread_ptr -> __mw_threadx_tls = 0; \ - thread_ptr -> __mw_errnum = 0; \ - thread_ptr -> __mw_thread_exit = TX_NULL; -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) \ - if (thread_ptr -> __mw_thread_exit) \ - (thread_ptr -> __mw_thread_exit) (thread_ptr); -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) \ - if (thread_ptr -> __mw_thread_exit) \ - (thread_ptr -> __mw_thread_exit) (thread_ptr); - -#else - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) - -#endif - - -/* Define the ThreadX object creation extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) -#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) -#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) -#define TX_TIMER_CREATE_EXTENSION(timer_ptr) - - -/* Define the ThreadX object deletion extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) -#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) -#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) -#define TX_TIMER_DELETE_EXTENSION(timer_ptr) - - -/************* Define ThreadX SMP data types and function prototypes. *************/ - -struct TX_THREAD_STRUCT; - - -/* Define the ThreadX SMP protection structure. */ - -typedef struct TX_THREAD_SMP_PROTECT_STRUCT -{ - ULONG tx_thread_smp_protect_in_force; - struct TX_THREAD_STRUCT * - tx_thread_smp_protect_thread; - ULONG tx_thread_smp_protect_core; - ULONG tx_thread_smp_protect_count; - - /* Implementation specific information follows. */ - - ULONG tx_thread_smp_protect_get_caller; - ULONG tx_thread_smp_protect_status32; - ULONG tx_thread_smp_protect_release_caller; -} TX_THREAD_SMP_PROTECT; - - - -/* Define ThreadX SMP low-level assembly routines. */ - -struct TX_THREAD_STRUCT * _tx_thread_smp_current_thread_get(void); -UINT _tx_thread_smp_protect(void); -void _tx_thread_smp_unprotect(UINT interrupt_save); -ULONG _tx_thread_smp_current_state_get(void); -ULONG _tx_thread_smp_time_get(void); - - -/* Determine if SMP Debug is selected. If so, the function prototype is setup. Otherwise, the debug call is - simply mapped to whitespace. */ - -#ifdef TX_THREAD_SMP_DEBUG_ENABLE -void _tx_thread_smp_debug_entry_insert(ULONG id, ULONG suspend, VOID *thread_ptr); -#else -#define _tx_thread_smp_debug_entry_insert(a, b, c) -#endif - - -/* Define the get thread macro. */ - -#define TX_THREAD_GET_CURRENT(a) a = (TX_THREAD *) _tx_thread_smp_current_thread_get(); - - -/* Define the get core ID macro. */ - -#define TX_SMP_CORE_ID _tx_thread_smp_core_get() - - - -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ - -#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save; - -#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); -#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); - - -/************* End ThreadX SMP data type and function prototype definitions. *************/ - - -/* Define the interrupt lockout macros for each ThreadX object. */ - -#define TX_BLOCK_POOL_DISABLE TX_DISABLE -#define TX_BYTE_POOL_DISABLE TX_DISABLE -#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE -#define TX_MUTEX_DISABLE TX_DISABLE -#define TX_QUEUE_DISABLE TX_DISABLE -#define TX_SEMAPHORE_DISABLE TX_DISABLE - - -/* Define the version ID of ThreadX. This may be utilized by the application. */ - -#ifdef TX_THREAD_INIT -CHAR _tx_version_id[] = - "Copyright (c) 1996-YYYY Express Logic Inc. * ThreadX SMP/ARC_HS/MetaWare Version GVVVV.5.0 SN: ZZZZ *"; -#else -extern CHAR _tx_version_id[]; -#endif - - -#endif - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.indexes/properties.index b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.indexes/properties.index deleted file mode 100644 index 9cb4d44f0f45a164bfef4ff7727285fab28b9ce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 411 zcmZvY%}&EG41nE&CQj3k#(@*s3z+_BV?9rBL})w!iW0AlXq(8%V10Yic89Iv;DhbY zpKM0}5Qmt>urypE$y!@SD3wQPnBdMT)4Yp@VC6((Yx?pPlTas2*G0BYlWmc0H#zoL z43ASsBff*I7dEx^(jT$67NiV(B+%1Y4G1q{7%L&zplXpS~iy0cW6aPSdlI( z54UMjq|tE7azgxS<*NYX#byK+6W|0ld7Ay->q0LnVD>CnqJ$`hm&*f>8g=W5TTvrZ msh#Hz_vFlxCA})`4e!ron2-6#d3{)2iu!K{84V-28__>xF@hBU diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.markers.snap b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/sample_threadx/.markers.snap deleted file mode 100644 index 0b368ce14fbcdffc79d020bc00b88646cda52674..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 acmZ?R*xjhShe1S2b=vdAllRFvpz#6ORt3WnSw*$8B z@=lklxt}1<>yn=;eiBO$z+=$9`;;BQF-=$qQHrK0&vjkpxF7~ETaixw%tnjNt9FC( zBSq3*O7=a)d&`GwY0ccvnS)}$(ga5g(Fcm@hCv!aOU~E?StC}J`Qy#PuhYpwcFy^t Q%HV0kC&-HCA9h>WANhM?e*gdg diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/tx/.markers.snap b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.projects/tx/.markers.snap deleted file mode 100644 index 0b368ce14fbcdffc79d020bc00b88646cda52674..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 acmZ?R*xjhShe1S2b=vdAllRFvpz#6ORt>$;?eHE=kNSVBj@0wKOm_HZ!p_H8wT}0JgLo@&Et; diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version deleted file mode 100644 index 6b2aaa76..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.indexes/properties.version +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.markers.snap b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/.root/.markers.snap deleted file mode 100644 index 0b368ce14fbcdffc79d020bc00b88646cda52674..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32 acmZ?R*xjhShe1S2b=vdAllRFvpz#6ORtI znHnnuxP&Me85kMxawX@aCKkD*=9DDH=NF~xr6%WO78IxIC8w0=CFd8V+LjcR0u8~T zDkU|kG+nPW(>5(Jrx>U#1!RtEML}j!Vo7Fx9(EHAO)ZT~4GoPfjSWmK2^fv!1bw}d zqSRDdBXeG^0};PJgMA$Mxu<$}`7sBYh5*!W0~!QSH&87MjEt=eOlg%0@P-sEV$|5c K%Ft*K`vw4SPevvH diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/27.snap b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.resources/27.snap deleted file mode 100644 index 8a37d2c1aa90e29ebef30a0cf226b44213c1c374..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25958 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 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prefs deleted file mode 100644 index 77ca49ab..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -macros/workspace=\r\n\r\n diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-sample_threadx.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-sample_threadx.prefs deleted file mode 100644 index 9c00dc4e..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-sample_threadx.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -indexer/preferenceScope=0 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-tx.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-tx.prefs deleted file mode 100644 index 9c00dc4e..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.core.prj-tx.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -indexer/preferenceScope=0 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs deleted file mode 100644 index 9531fc3c..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.core.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.cdt.debug.core.cDebug.default_source_containers=\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n -org.eclipse.cdt.debug.corecDebug.Disassembly.instructionStepOn=true diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.ui.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.ui.prefs deleted file mode 100644 index c03690a1..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.debug.ui.prefs +++ /dev/null @@ -1,7 +0,0 @@ -columnOrderKeyEXE=0,1,2,3,4,5 -columnOrderKeySF=0,1,2,3,4,5 -columnSortDirectionKeyEXE=128 -columnSortDirectionKeySF=128 -eclipse.preferences.version=1 -visibleColumnsKeyEXE=1,1,1,0,0,0 -visibleColumnsKeySF=1,1,0,0,0,0 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.launchbar.core.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.launchbar.core.prefs deleted file mode 100644 index c002721a..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.launchbar.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -activeConfigDesc=sample_threadx Debug.org.eclipse.cdt.launchbar.core.descriptor.default -configDescList=[sample_threadx Debug.org.eclipse.cdt.launchbar.core.descriptor.default] -sample_threadx\ Debug.org.eclipse.cdt.launchbar.core.descriptor.default/activeLaunchMode=debug -sample_threadx\ Debug.org.eclipse.cdt.launchbar.core.descriptor.default/activeLaunchTarget=org.eclipse.cdt.launchbar.core.target.local -eclipse.preferences.version=1 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.managedbuilder.core.prefs deleted file mode 100644 index cc2e0070..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.managedbuilder.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -eclipse.preferences.version=1 -properties/sample_threadx.com.arc.cdt.toolchain.arc.av2hs.exeProject.1700533761/com.arc.cdt.toolchain.av2hs.exeDebugConfig.585788724=av2hs.exe.debug.exeCompilerDebug.1743110770\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.exeDebugConfig.585788724\=rcState\\\=0\\r\\nrebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.asmDebugExe.1483523628\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.exeToolChainDebug.1358823635\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.arc.archiver.886382681\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.exeLinkerDebug.672502322\=rebuildState\\\=false\\r\\n\r\n -properties/sample_threadx.com.arc.cdt.toolchain.arc.av2hs.exeProject.1700533761/com.arc.cdt.toolchain.av2hs.exeReleaseConfig.2024992869=com.arc.cdt.toolchain.av2hs.exelinkerRelease.934772409\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.asmReleaseExe.813382130\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.exeReleaseToolChain.202924782\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.arc.archiver.990750758\=rebuildState\\\=true\\r\\n\r\narc.cdt.toolchain.av2hs.exeCompilerRelease.1463268267\=rebuildState\\\=true\\r\\n\r\n -properties/tx.com.arc.cdt.toolchain.arc.av2hs.libProject.1128858457/com.arc.cdt.toolchain.av2hs.libDebugConfig.2063275274=com.arc.cdt.toolchain.av2hs.libDebugAsm.1626881776\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.ArDebug.178841002\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.libDebugConfig.2063275274\=rcState\\\=0\\r\\nrebuildState\\\=false\\r\\n\r\nav2hs.lib.debug.libCompiler.46227008\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.ArDebug.1591578035\=rebuildState\\\=false\\r\\n\r\nav2hs.lib.debug.libCompiler.2145942775\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.libDebugToolChain.22686690\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.arc.Linker.141619666\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.libDebugToolChain.1385404397\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.arc.Linker.41800372\=rebuildState\\\=false\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.libDebugAsm.262854485\=rebuildState\\\=true\\r\\n\r\n -properties/tx.com.arc.cdt.toolchain.arc.av2hs.libProject.1128858457/com.arc.cdt.toolchain.av2hs.libReleaseConfig.1202427021=com.arc.cdt.toolchain.av2hs.libCompilerRelease.1920721386\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.libReleaseAsm.1207600374\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.ArRelease.217147730\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.av2hs.libReleaseToolChain.1456119623\=rebuildState\\\=true\\r\\n\r\ncom.arc.cdt.toolchain.arc.Linker.1382145468\=rebuildState\\\=true\\r\\n\r\n diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prefs deleted file mode 100644 index 5e2da66d..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -spelling_locale_initialized=true -useAnnotationsPrefPage=true -useQuickDiffPrefPage=true diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-sample_threadx.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-sample_threadx.prefs deleted file mode 100644 index d6d44177..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-sample_threadx.prefs +++ /dev/null @@ -1,3 +0,0 @@ -buildConsole/keepLog=false -buildConsole/logLocation=C\:\\arc_hs_smp\\.metadata\\.plugins\\org.eclipse.cdt.ui\\sample_threadx.build.log -eclipse.preferences.version=1 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-tx.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-tx.prefs deleted file mode 100644 index d098905e..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.cdt.ui.prj-tx.prefs +++ /dev/null @@ -1,3 +0,0 @@ -buildConsole/keepLog=false -buildConsole/logLocation=C\:\\arc_hs_smp\\.metadata\\.plugins\\org.eclipse.cdt.ui\\tx.build.log -eclipse.preferences.version=1 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index dffc6b51..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -version=1 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.core.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.core.prefs deleted file mode 100644 index 7bc76be7..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.core.prefs +++ /dev/null @@ -1,5 +0,0 @@ -//org.eclipse.debug.core.PREFERRED_DELEGATES/org.eclipse.cdt.launch.attachLaunchType=org.eclipse.cdt.dsf.gdb.launch.attachCLaunch,debug,; -//org.eclipse.debug.core.PREFERRED_DELEGATES/org.eclipse.cdt.launch.localCLaunch=org.eclipse.cdt.cdi.launch.localCLaunch,run,; -//org.eclipse.debug.core.PREFERRED_DELEGATES/org.eclipse.cdt.launch.postmortemLaunchType=org.eclipse.cdt.dsf.gdb.launch.coreCLaunch,debug,; -eclipse.preferences.version=1 -prefWatchExpressions=\r\n\r\n diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.ui.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.ui.prefs deleted file mode 100644 index 38703762..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.debug.ui.prefs +++ /dev/null @@ -1,9 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.debug.ui.PREF_LAUNCH_PERSPECTIVES=\r\n\r\n -org.eclipse.debug.ui.user_view_bindings=\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n -pref_state_memento.org.eclipse.debug.ui.BreakpointView=\r\n\r\n\r\n\r\n\r\n -pref_state_memento.org.eclipse.debug.ui.DebugVieworg.eclipse.debug.ui.DebugView=\r\n -pref_state_memento.org.eclipse.debug.ui.ModuleView=\r\n -pref_state_memento.org.eclipse.debug.ui.VariableView=\r\n -preferredDetailPanes=DefaultDetailPane\:DefaultDetailPane| -preferredTargets=org.eclipse.cdt.debug.ui.toggleCBreakpointTarget\:org.eclipse.cdt.debug.ui.toggleCBreakpointTarget|org.eclipse.cdt.debug.ui.toggleCBreakpointTarget,org.eclipse.cdt.debug.ui.toggleCDynamicPrintfTarget\:org.eclipse.cdt.debug.ui.toggleCBreakpointTarget| diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs deleted file mode 100644 index 61f3bb8b..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.editors.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -overviewRuler_migration=migrated_3.1 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs deleted file mode 100644 index 76ce67b9..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.ide.prefs +++ /dev/null @@ -1,5 +0,0 @@ -PROBLEMS_FILTERS_MIGRATE=true -eclipse.preferences.version=1 -platformState=1590536495337 -quickStart=true -tipsAndTricks=true diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs deleted file mode 100644 index 08076f23..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.prefs +++ /dev/null @@ -1,2 +0,0 @@ -eclipse.preferences.version=1 -showIntro=false diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs deleted file mode 100644 index e2e3209c..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.core.runtime/.settings/org.eclipse.ui.workbench.prefs +++ /dev/null @@ -1,3 +0,0 @@ -//org.eclipse.ui.commands/state/org.eclipse.ui.navigator.resources.nested.changeProjectPresentation/org.eclipse.ui.commands.radioState=false -UIActivities.org.eclipse.cdt.debug.cdigdbActivity=true -eclipse.preferences.version=1 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.core/.launches/sample_threadx Debug.launch b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.core/.launches/sample_threadx Debug.launch deleted file mode 100644 index 077481e2..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.core/.launches/sample_threadx Debug.launch +++ /dev/null @@ -1,476 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/dialog_settings.xml deleted file mode 100644 index c13038c2..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/dialog_settings.xml +++ /dev/null @@ -1,11 +0,0 @@ - -
-
- - - - - - -
-
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/launchConfigurationHistory.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/launchConfigurationHistory.xml deleted file mode 100644 index c22cfeb5..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.debug.ui/launchConfigurationHistory.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi deleted file mode 100644 index a0b79e26..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi +++ /dev/null @@ -1,2041 +0,0 @@ - - - - activeSchemeId:org.eclipse.ui.defaultAcceleratorConfiguration - ModelMigrationProcessor.001 - - - - - - - - topLevel - shellMaximized - - - - - persp.actionSet:com.arc.eclipse.aboutMWDebugger - persp.actionSet:com.arc.cdt.toolchain.PDFs - persp.actionSet:org.eclipse.ui.cheatsheets.actionSet - persp.actionSet:org.eclipse.search.searchActionSet - persp.actionSet:org.eclipse.ui.edit.text.actionSet.annotationNavigation - persp.actionSet:org.eclipse.ui.edit.text.actionSet.navigation - persp.actionSet:org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo - persp.actionSet:org.eclipse.ui.externaltools.ExternalToolsSet - persp.actionSet:org.eclipse.ui.actionSet.keyBindings - persp.actionSet:org.eclipse.ui.actionSet.openFiles - persp.actionSet:org.eclipse.cdt.ui.SearchActionSet - persp.actionSet:org.eclipse.cdt.ui.CElementCreationActionSet - persp.actionSet:org.eclipse.ui.NavigateActionSet - persp.viewSC:org.eclipse.ui.console.ConsoleView - persp.viewSC:org.eclipse.search.ui.views.SearchView - persp.viewSC:org.eclipse.ui.views.ContentOutline - persp.viewSC:org.eclipse.ui.views.ProblemView - persp.viewSC:org.eclipse.cdt.ui.CView - persp.viewSC:org.eclipse.ui.views.ResourceNavigator - persp.viewSC:org.eclipse.ui.views.PropertySheet - persp.viewSC:org.eclipse.ui.views.TaskList - persp.newWizSC:org.eclipse.cdt.ui.wizards.ConvertToMakeWizard - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewMakeFromExisting - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewCWizard1 - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewCWizard2 - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewSourceFolderCreationWizard - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewFolderCreationWizard - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewSourceFileCreationWizard - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewHeaderFileCreationWizard - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewFileCreationWizard - persp.newWizSC:org.eclipse.cdt.ui.wizards.NewClassCreationWizard - persp.viewSC:org.eclipse.pde.runtime.LogView - persp.showIn:org.eclipse.cdt.codan.internal.ui.views.ProblemDetails - persp.viewSC:org.eclipse.cdt.codan.internal.ui.views.ProblemDetails - persp.actionSet:org.eclipse.debug.ui.breakpointActionSet - persp.viewSC:org.eclipse.cdt.make.ui.views.MakeView - persp.actionSet:org.eclipse.cdt.make.ui.makeTargetActionSet - persp.perspSC:org.eclipse.debug.ui.DebugPerspective - persp.perspSC:org.eclipse.team.ui.TeamSynchronizingPerspective - persp.actionSet:org.eclipse.debug.ui.launchActionSet - persp.actionSet:org.eclipse.cdt.ui.buildConfigActionSet - persp.actionSet:org.eclipse.cdt.ui.NavigationActionSet - persp.actionSet:org.eclipse.cdt.ui.OpenActionSet - persp.actionSet:org.eclipse.cdt.ui.CodingActionSet - persp.actionSet:org.eclipse.ui.edit.text.actionSet.presentation - persp.showIn:org.eclipse.cdt.ui.includeBrowser - persp.showIn:org.eclipse.cdt.ui.CView - persp.showIn:org.eclipse.ui.navigator.ProjectExplorer - persp.viewSC:org.eclipse.ui.navigator.ProjectExplorer - persp.viewSC:org.eclipse.cdt.ui.includeBrowser - - - newtablook - - - - - - - - - - newtablook - - - - - - - newtablook - - - - - - - - - - - persp.actionSet:com.arc.eclipse.aboutMWDebugger - persp.actionSet:com.arc.cdt.toolchain.PDFs - persp.actionSet:org.eclipse.ui.cheatsheets.actionSet - persp.actionSet:org.eclipse.search.searchActionSet - persp.actionSet:org.eclipse.ui.edit.text.actionSet.annotationNavigation - persp.actionSet:org.eclipse.ui.edit.text.actionSet.navigation - persp.actionSet:org.eclipse.ui.edit.text.actionSet.convertLineDelimitersTo - persp.actionSet:org.eclipse.ui.externaltools.ExternalToolsSet - persp.actionSet:org.eclipse.ui.actionSet.keyBindings - persp.actionSet:org.eclipse.ui.actionSet.openFiles - persp.actionSet:org.eclipse.debug.ui.launchActionSet - persp.actionSet:org.eclipse.debug.ui.debugActionSet - persp.viewSC:org.eclipse.debug.ui.DebugView - persp.viewSC:org.eclipse.debug.ui.VariableView - persp.viewSC:org.eclipse.debug.ui.BreakpointView - persp.viewSC:org.eclipse.debug.ui.ExpressionView - persp.viewSC:org.eclipse.ui.views.ContentOutline - persp.viewSC:org.eclipse.ui.console.ConsoleView - persp.viewSC:org.eclipse.ui.views.TaskList - persp.viewSC:com.arc.cdt.debug.seecode.ui.views.disasm - persp.viewSC:com.arc.cdt.debug.seecode.ui.command - persp.viewSC:com.arc.cdt.debug.seecode.ui.views.memsearch - persp.viewSC:com.arc.cdt.seecode.errorlog - persp.viewSC:org.eclipse.cdt.debug.ui.SignalsView - persp.viewSC:org.eclipse.cdt.debug.ui.RegisterView - persp.viewSC:org.eclipse.debug.ui.ModuleView - persp.viewSC:org.eclipse.debug.ui.MemoryView - persp.viewSC:org.eclipse.ui.views.ProblemView - persp.viewSC:org.eclipse.cdt.debug.ui.executablesView - persp.actionSet:org.eclipse.cdt.debug.ui.debugActionSet - persp.actionSet:org.eclipse.cdt.debug.ui.debugActionSetExt - persp.viewSC:org.eclipse.cdt.dsf.gdb.ui.tracecontrol.view - persp.viewSC:org.eclipse.cdt.dsf.debug.ui.disassembly.view - persp.perspSC:org.eclipse.cdt.ui.CPerspective - persp.viewSC:org.eclipse.cdt.visualizer.view - persp.actionSet:org.eclipse.ui.NavigateActionSet - persp.actionSet:org.eclipse.debug.ui.breakpointActionSet - persp.viewSC:org.eclipse.pde.runtime.LogView - persp.actionSet:org.eclipse.cdt.debug.ui.debugActionSetExt2 - - - - - - newtablook - org.eclipse.e4.primaryNavigationStack - - - - - newtablook - - - - - newtablook - - - - - - - - - - - - - - - - - - - - - - - newtablook - - - - - - newtablook - org.eclipse.e4.secondaryNavigationStack - - - - - - - - - Standalone - - - - - - - newtablook - org.eclipse.e4.secondaryDataStack - - - - - - - - - - - newtablook - - - - - - - - - - - - - - - - - View - categoryTag:Help - - - View - categoryTag:General - - ViewMenu - menuContribution:menu - - - - - View - categoryTag:Help - - - - newtablook - org.eclipse.e4.primaryDataStack - EditorStack - active - noFocus - - - Editor - org.eclipse.cdt.ui.editor.CEditor - removeOnHide - active - - menuContribution:popup - popup:#CEditorContext - popup:org.eclipse.cdt.ui.editor.CEditor.EditorContext - popup:#AbstractTextEditorContext - - - menuContribution:popup - popup:#CEditorRulerContext - popup:org.eclipse.cdt.ui.editor.CEditor.RulerContext - popup:#AbstractTextEditorRulerContext - - - menuContribution:popup - popup:#OverviewRulerContext - - - - - - - View - categoryTag:General - - ViewMenu - menuContribution:menu - - - menuContribution:popup - popup:org.eclipse.ui.navigator.ProjectExplorer#PopupMenu - - - menuContribution:popup - popup:org.eclipse.ui.navigator.ProjectExplorer#PopupMenu - - - menuContribution:popup - popup:org.eclipse.ui.navigator.ProjectExplorer#PopupMenu - - - - - View - categoryTag:&C/C++ - - - View - categoryTag:General - - - View - categoryTag:General - - - - View - categoryTag:General - - ViewMenu - menuContribution:menu - - - menuContribution:popup - popup:org.eclipse.ui.views.ProblemView - popup:org.eclipse.ui.ide.MarkersView - - - menuContribution:popup - popup:org.eclipse.ui.views.ProblemView - popup:org.eclipse.ui.ide.MarkersView - - - menuContribution:popup - popup:org.eclipse.ui.views.ProblemView - popup:org.eclipse.ui.ide.MarkersView - - - - - View - categoryTag:General - - - - View - categoryTag:General - - ViewMenu - menuContribution:menu - - - menuContribution:popup - popup:org.eclipse.cdt.ui.CDTGlobalBuildConsole - - - menuContribution:popup - popup:org.eclipse.cdt.ui.CDTBuildConsole - - - menuContribution:popup - popup:org.eclipse.cdt.ui.CDTGlobalBuildConsole - - - menuContribution:popup - popup:org.eclipse.cdt.ui.CDTBuildConsole - - - menuContribution:popup - popup:org.eclipse.cdt.ui.CDTGlobalBuildConsole - - - menuContribution:popup - popup:org.eclipse.cdt.ui.CDTBuildConsole - - - - - View - categoryTag:General - - - - View - categoryTag:General - - ViewMenu - menuContribution:menu - - - menuContribution:popup - popup:#ASMOutlineContext - - - menuContribution:popup - popup:#ASMOutlineContext - - - menuContribution:popup - popup:#TranslationUnitOutlinerContext - - - menuContribution:popup - popup:#TranslationUnitOutlinerContext - - - menuContribution:popup - popup:#TranslationUnitOutlinerContext - - - menuContribution:popup - popup:#TranslationUnitOutlinerContext - - - menuContribution:popup - popup:#TranslationUnitOutlinerContext - - - - - View - categoryTag:Make - - - View - categoryTag:Terminal - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - View - categoryTag:Debug - - ViewMenu - menuContribution:menu - - - - - - toolbarSeparator - - - - Draggable - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - - toolbarSeparator - - - - Draggable - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - - Draggable - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - - Draggable - - Opaque - - - Opaque - - - Opaque - - - - Draggable - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - - toolbarSeparator - - - - Draggable - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - Opaque - - - - Opaque - - - Opaque - - - Opaque - - - - Draggable - - - toolbarSeparator - - - - toolbarSeparator - - - - Draggable - - Opaque - - - Opaque - - - - stretch - SHOW_RESTORE_MENU - - - Draggable - HIDEABLE - SHOW_RESTORE_MENU - - - - - stretch - - - Draggable - - - Draggable - - - - - TrimStack - - - TrimStack - - - - - TrimStack - - - TrimStack - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - platform:win32 - - - - - - - - - - - - - - - - - platform:win32 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Editor - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:&C/C++ - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Make - - - - - View - categoryTag:&C/C++ - - - - - View - categoryTag:&C/C++ - - - - - View - categoryTag:&C/C++ - - - - - View - categoryTag:&C/C++ - - - - - View - categoryTag:&C/C++ - - - - - View - categoryTag:General - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Debug - - - - - View - categoryTag:Help - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:Team - - - - - View - categoryTag:Team - - - - - View - categoryTag:Terminal - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:Help - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - View - categoryTag:General - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.history b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.history deleted file mode 100644 index 5c2a14e3..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.history +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.index b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.index deleted file mode 100644 index 53cdadda..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2017/4/15/refactorings.index +++ /dev/null @@ -1,8 +0,0 @@ -1491970036256 Delete resource 'tx/tx_thread_context_fast_restore.s' -1491970041634 Delete resource 'tx/tx_thread_context_fast_save.s' -1491970051557 Delete resource 'tx/tx_thread_register_bank_assign.s' -1491970093291 Delete resource 'tx/tx_initialize_fast_interrupt_setup.s' -1492209727732 Delete resource 'sample_threadx/tx_port_test_threads.s' -1492209734183 Delete resource 'sample_threadx/arc.bak' -1492209738993 Delete resource 'sample_threadx/sample_threadx.bak' -1492209743831 Delete resource 'sample_threadx/tx_initialize_low_level.bak' diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.history b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.history deleted file mode 100644 index fb3ddb55..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.history +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.index b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.index deleted file mode 100644 index 3d6f8352..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.core.refactoring/.refactorings/.workspace/2020/6/25/refactorings.index +++ /dev/null @@ -1,3 +0,0 @@ -1592510876244 Delete resource 'demo_threadx' -1592511041806 Delete 2 resources -1592511103706 Delete resource 'tx/src_generic' diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.ui.refactoring/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.ui.refactoring/dialog_settings.xml deleted file mode 100644 index aa267842..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ltk.ui.refactoring/dialog_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - -
-
- - -
-
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.editors/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.editors/dialog_settings.xml deleted file mode 100644 index 50f1edb3..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.editors/dialog_settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - -
-
-
-
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml deleted file mode 100644 index ffb12cc6..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.ide/dialog_settings.xml +++ /dev/null @@ -1,22 +0,0 @@ - -
-
- - - - - - - - - -
-
- - -
-
- - -
-
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml deleted file mode 100644 index 2504009a..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/dialog_settings.xml +++ /dev/null @@ -1,31 +0,0 @@ - -
-
- - - - - - - - - - -
-
- - - - -
-
- - - - - - - - -
-
diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml deleted file mode 100644 index 373b8d71..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.ui.workbench/workingsets.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/version.ini b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/version.ini deleted file mode 100644 index 0c03ef25..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/version.ini +++ /dev/null @@ -1,3 +0,0 @@ -#Thu Jun 18 13:20:39 PDT 2020 -org.eclipse.core.runtime=2 -org.eclipse.platform=4.6.3.v20170301-0400 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/language.settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/language.settings.xml deleted file mode 100644 index b88c974a..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/language.settings.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/org.eclipse.cdt.codan.core.prefs b/ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/org.eclipse.cdt.codan.core.prefs deleted file mode 100644 index 474b8a1e..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/sample_threadx/.settings/org.eclipse.cdt.codan.core.prefs +++ /dev/null @@ -1,69 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.cdt.codan.checkers.errnoreturn=Warning -org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} -org.eclipse.cdt.codan.checkers.errreturnvalue=Error -org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.checkers.noreturn=Error -org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} -org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error -org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error -org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning -org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error -org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning -org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} -org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning -org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} -org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error -org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning -org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} -org.eclipse.cdt.codan.internal.checkers.ExternalBindingProblem=Warning -org.eclipse.cdt.codan.internal.checkers.ExternalBindingProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error -org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error -org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error -org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error -org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error -org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error -org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error -org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info -org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} -org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning -org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error -org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error -org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error -org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning -org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning -org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning -org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} -org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning -org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} -org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning -org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} -org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error -org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} -org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning -org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} -org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning -org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} -org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning -org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} -org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error -org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} diff --git a/ports_smp/arc_hs_smp/metaware/example_build/tx/.settings/language.settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/tx/.settings/language.settings.xml deleted file mode 100644 index fbb81b1a..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/tx/.settings/language.settings.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..aa89d53a --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..9aefbdcd --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a34_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..3c1c5962 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a34_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a34_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a34_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a34_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a34_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..d4507819 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..4feeca33 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_timeout.c b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_initialize_wait.S similarity index 51% rename from ports_smp/cortex_a35_smp/gnu/src/tx_thread_timeout.c rename to ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_initialize_wait.S index 09be1086..c806c02f 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_timeout.c +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -15,42 +15,33 @@ /** */ /** ThreadX Component */ /** */ -/** Thread */ +/** Thread - Low Level SMP Support */ /** */ /**************************************************************************/ /**************************************************************************/ -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - + .text + .align 3 /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_timeout Cortex-A35-SMP */ -/* 6.1.9 */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ /* */ /* INPUT */ /* */ -/* timeout_input Contains the thread pointer */ +/* None */ /* */ /* OUTPUT */ /* */ @@ -58,109 +49,91 @@ /* */ /* CALLS */ /* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ +/* _tx_thread_schedule Thread scheduling loop */ /* */ /* CALLED BY */ /* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ +/* Hardware */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: -TX_INTERRUPT_SAVE_AREA + /* Lockout interrupts. */ -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; + MSR DAIFSet, 0x3 // Lockout interrupts + /* Pickup the Core ID. */ - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID #else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID #endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID #endif - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ -#ifndef TX_NOT_INTERRUPTABLE + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else + /* Save the system stack pointer for this core. */ - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer -#ifndef TX_NOT_INTERRUPTABLE - /* Restore interrupts. */ - TX_RESTORE -#endif + /* Pickup the release cores flag. */ - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } + /* Core 0 has released this core. */ -#ifdef TX_NOT_INTERRUPTABLE + /* Clear this core's system state variable. */ - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a34_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a34_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a34_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..d583631e --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a34_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..e8013d86 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a34_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a34_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a34_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a34_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a34_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..d4507819 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..4feeca33 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_module/cortex_a35_smp/ac6/module_manager/src/tx_thread_timeout.c b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_initialize_wait.S similarity index 51% rename from ports_module/cortex_a35_smp/ac6/module_manager/src/tx_thread_timeout.c rename to ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_initialize_wait.S index 00f48e14..c806c02f 100644 --- a/ports_module/cortex_a35_smp/ac6/module_manager/src/tx_thread_timeout.c +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -15,42 +15,33 @@ /** */ /** ThreadX Component */ /** */ -/** Thread */ +/** Thread - Low Level SMP Support */ /** */ /**************************************************************************/ /**************************************************************************/ -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - + .text + .align 3 /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_timeout Cortex-A35-SMP */ -/* 6.1 */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ /* */ /* INPUT */ /* */ -/* timeout_input Contains the thread pointer */ +/* None */ /* */ /* OUTPUT */ /* */ @@ -58,107 +49,91 @@ /* */ /* CALLS */ /* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ +/* _tx_thread_schedule Thread scheduling loop */ /* */ /* CALLED BY */ /* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ +/* Hardware */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: -TX_INTERRUPT_SAVE_AREA + /* Lockout interrupts. */ -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; + MSR DAIFSet, 0x3 // Lockout interrupts + /* Pickup the Core ID. */ - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID #else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID #endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID #endif - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ -#ifndef TX_NOT_INTERRUPTABLE + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else + /* Save the system stack pointer for this core. */ - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer -#ifndef TX_NOT_INTERRUPTABLE - /* Restore interrupts. */ - TX_RESTORE -#endif + /* Pickup the release cores flag. */ - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } + /* Core 0 has released this core. */ -#ifdef TX_NOT_INTERRUPTABLE + /* Clear this core's system state variable. */ - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a34_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a34_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a34_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/GICv3_gicr.c index 4b47caeb..d91aeb27 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/GICv3_gicr.c +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -1,7 +1,7 @@ /* * GICv3_gicr.c - generic driver code for GICv3 redistributor * - * Copyright (c) 2014-2018 Arm Limited (or its affiliates). All rights reserved. + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. * Use, modification and redistribution of this file is subject to your possession of a * valid End User License Agreement for the Arm Product of which these examples are part of * and your compliance with all applicable terms and conditions of such licence agreement. @@ -129,6 +129,25 @@ static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) /**********************************************************************/ +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + void WakeupGICR(uint32_t gicr) { GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.S index 7b63558e..c787c3f5 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.S +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -1,7 +1,8 @@ // // Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A // -// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -43,6 +44,7 @@ _mutex_initialize: ret .cfi_endproc +#if !defined(USE_LSE_ATOMIC) .type _mutex_acquire, "function" .cfi_startproc @@ -84,3 +86,48 @@ _mutex_release: stlr w1, [x0] ret .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.c index b766cf71..17cceb01 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.c +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -3,19 +3,22 @@ byte pool, and block pool. */ #include "tx_api.h" -#include + extern void init_timer(void); /* in timer_interrupts.c */ -#define DEMO_STACK_SIZE 1024 -#define DEMO_BYTE_POOL_SIZE 0x20000 -#define DEMO_BLOCK_POOL_SIZE 100 -#define DEMO_QUEUE_SIZE 100 + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 -/* Define a memory area to create a byte pool in. */ +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + -UCHAR memory_area[DEMO_BYTE_POOL_SIZE] __attribute__((aligned (8))); /* Define the ThreadX object control blocks... */ @@ -68,6 +71,8 @@ UCHAR event_buffer[65536]; #endif +/* Define main entry point. */ + int main(void) { @@ -95,7 +100,7 @@ CHAR *pointer = TX_NULL; #endif /* 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); + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); /* Allocate the stack for thread 0. */ tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); @@ -386,4 +391,3 @@ UINT status; break; } } - diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.launch index 9aefbdcd..5a0bdcf4 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.launch +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -6,17 +6,17 @@ - + - + - + - + - + @@ -24,11 +24,11 @@ - + - + - + @@ -36,40 +36,41 @@ - - - - + + + + - + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - + - + + @@ -89,15 +90,15 @@ - + - + - + @@ -105,13 +106,13 @@ - + - + @@ -119,7 +120,7 @@ - + @@ -162,6 +163,8 @@ + + @@ -221,7 +224,7 @@ - + @@ -363,7 +366,7 @@ - + @@ -386,7 +389,7 @@ - + @@ -395,6 +398,7 @@ + diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/startup.S index d0be1cf9..ee87d101 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/startup.S +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/startup.S @@ -5,7 +5,7 @@ // // Exits in EL1 AArch64 // -// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -19,7 +19,6 @@ .balign 4 .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame - .global el1_vectors .global el2_vectors .global el3_vectors @@ -31,6 +30,7 @@ .global SetSPISecurityAll .global SetPrivateIntPriority + .global GetGICR .global WakeupGICR .global SyncAREinGICD .global EnableGICD @@ -533,7 +533,7 @@ el1_primary: // VA->PA translation // bic x4, x4, #((1 << 21) - 1) - mov x1, #(TT_S1_ATTR_BLOCK | \ + ldr x1, =(TT_S1_ATTR_BLOCK | \ (1 << TT_S1_ATTR_MATTR_LSB) | \ TT_S1_ATTR_NS | \ TT_S1_ATTR_AP_RW_PL1 | \ @@ -622,7 +622,7 @@ nol2setup: // translation // bic x4, x4, #((1 << 21) - 1) // start address mod 2MB - mov x1, #(TT_S1_ATTR_BLOCK | \ + ldr x1, =(TT_S1_ATTR_BLOCK | \ (2 << TT_S1_ATTR_MATTR_LSB) | \ TT_S1_ATTR_NS | \ TT_S1_ATTR_AP_RW_PL1 | \ @@ -657,7 +657,7 @@ nol2setup: // translation // bic x4, x4, #((1 << 21) - 1) // start address mod 2MB - mov x1, #(TT_S1_ATTR_BLOCK | \ + ldr x1, =(TT_S1_ATTR_BLOCK | \ (2 << TT_S1_ATTR_MATTR_LSB) | \ TT_S1_ATTR_NS | \ TT_S1_ATTR_AP_RW_PL1 | \ diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/timer_interrupts.c index 7b0996ef..8f522217 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/timer_interrupts.c +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -2,10 +2,10 @@ /* Timer and interrupts */ -/* Copyright (c) 2016 Arm Limited (or its affiliates). All rights reserved. */ -/* Use, modification and redistribution of this file is subject to your */ -/* possession of a valid DS-5 end user licence agreement and your compliance */ -/* with all applicable terms and conditions of such licence agreement. */ +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ #include @@ -112,14 +112,14 @@ void fiqHandler(void) unsigned int aliased = 0; ID = getICC_IAR0(); // readIntAck(); - printf("fiqHandler() - Read %d from IAR0\n", ID); + //printf("fiqHandler() - Read %d from IAR0\n", ID); // Check for reserved IDs if ((1020 <= ID) && (ID <= 1023)) { - printf("fiqHandler() - Reserved INTID %d\n\n", ID); + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); ID = getICC_IAR1(); // readAliasedIntAck(); - printf("fiqHandler() - Read %d from AIAR\n", ID); + //printf("fiqHandler() - Read %d from AIAR\n", ID); aliased = 1; // If still spurious then simply return @@ -131,13 +131,13 @@ void fiqHandler(void) { case 34: // Dual-Timer 0 (SP804) - printf("fiqHandler() - External timer interrupt\n\n"); + //printf("fiqHandler() - External timer interrupt\n\n"); clearTimerIrq(); break; default: // Unexpected ID value - printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); break; } diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.S index f39eca6c..45445a98 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.S +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -1,7 +1,7 @@ // ------------------------------------------------------------ // Armv8-A AArch64 - Common helper functions // -// Copyright (c) 2012-2018 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -17,6 +17,7 @@ .global InvalidateUDCaches .global GetMIDR .global GetMPIDR + .global GetAffinity .global GetCPUID // ------------------------------------------------------------ @@ -138,12 +139,25 @@ GetMPIDR: .cfi_endproc + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + .type GetCPUID, "function" .cfi_startproc GetCPUID: mrs x0, MIDR_EL1 ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ cmp x0, #0xD0B // Cortex-A76 b.eq DynamIQ cmp x0, #0xD0A // Cortex-A75 @@ -158,6 +172,8 @@ DynamIQ: Others: mrs x0, MPIDR_EL1 - ubfx x0, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 ret .cfi_endproc diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_mmu.h index dbf903ca..bce62b54 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_mmu.h +++ b/ports_smp/cortex_a35_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -1,7 +1,7 @@ // // Defines for v8 Memory Model // -// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -90,10 +90,20 @@ #define TT_S1_ATTR_AF (1 << 10) #define TT_S1_ATTR_nG (1 << 11) +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + #define TT_S1_ATTR_CONTIG (1 << 52) #define TT_S1_ATTR_PXN (1 << 53) #define TT_S1_ATTR_UXN (1 << 54) +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + #define TT_S1_MAIR_DEV_nGnRnE 0b00000000 #define TT_S1_MAIR_DEV_nGnRE 0b00000100 #define TT_S1_MAIR_DEV_nGRE 0b00001000 diff --git a/ports_smp/cortex_a35_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a35_smp/ac6/example_build/tx/.cproject index 7b74f4e1..77ab8b02 100644 --- a/ports_smp/cortex_a35_smp/ac6/example_build/tx/.cproject +++ b/ports_smp/cortex_a35_smp/ac6/example_build/tx/.cproject @@ -7,7 +7,19 @@ - + + + + + + + + + + + + + @@ -105,9 +117,29 @@ + + + + + + + + + + + + + + + + + + + + - + diff --git a/ports_smp/cortex_a35_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a35_smp/ac6/inc/tx_port.h index fb058aa3..fcac1485 100644 --- a/ports_smp/cortex_a35_smp/ac6/inc/tx_port.h +++ b/ports_smp/cortex_a35_smp/ac6/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -48,12 +48,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ -/* 10-15-2021 William E. Lamie Modified comment(s), added */ -/* symbol ULONG64_DEFINED, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -408,8 +405,7 @@ typedef struct TX_THREAD_SMP_PROTECT_STRUCT #define TX_SEMAPHORE_DISABLE TX_DISABLE -/* Define VFP extension for the Cortex-A35. Each is assumed to be called in the context of the executing - thread. */ +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ #ifndef TX_SOURCE_CODE #define tx_thread_fp_enable _tx_thread_fp_enable @@ -424,7 +420,7 @@ VOID tx_thread_fp_disable(VOID); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A35-SMP/AC6 Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a35_smp/ac6/src/tx_initialize_low_level.S index fedf9f06..59fe38e0 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_initialize_low_level.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_initialize_low_level.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_initialize_low_level Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -62,8 +62,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_initialize_low_level(VOID) diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_restore.S index f58fd9f5..4df471ac 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_restore.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_restore.S @@ -30,8 +30,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_context_restore Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,9 +64,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_context_restore(VOID) @@ -79,7 +79,7 @@ _tx_thread_context_restore: MSR DAIFSet, 0x3 // Lockout interrupts -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the ISR exit function to indicate an ISR is complete. */ diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_save.S index 73fcfffd..79e33086 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_save.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_context_save.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_context_save Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -60,9 +60,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_context_save(VOID) @@ -135,7 +135,7 @@ _tx_thread_context_save: #endif STP x0, x1, [sp, #-16]! // Save SPSR, ELR -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the ISR enter function to indicate an ISR is executing. */ @@ -238,7 +238,7 @@ __tx_thread_idle_system_save: /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the ISR enter function to indicate an ISR is executing. */ diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_disable.c index 297b5de0..3e5d7e21 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_disable.c +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_disable.c @@ -33,8 +33,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_fp_disable Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,8 +64,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_thread_fp_disable(VOID) diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_enable.c index b1f84c6e..4e69205c 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_enable.c +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_fp_enable.c @@ -32,8 +32,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_fp_enable Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -63,8 +63,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_thread_fp_enable(VOID) diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_control.S index 68897ea1..6a5a7741 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_control.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_control.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_interrupt_control Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // UINT _tx_thread_interrupt_control(UINT new_posture) @@ -79,4 +79,3 @@ _tx_thread_interrupt_control: MOV x0, x1 // Setup return value RET // Return to caller // } - diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_disable.S index e6412d7b..d0062ef8 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_disable.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_disable.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_interrupt_disable Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,8 +58,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // UINT _tx_thread_interrupt_disable(void) diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_restore.S index 054efbda..1b6261ba 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_restore.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_interrupt_restore.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_interrupt_restore Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // UINT _tx_thread_interrupt_restore(UINT old_posture) diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_schedule.S index 7a59de4c..35a8fc96 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_schedule.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_schedule.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_schedule Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -62,9 +62,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -205,7 +205,7 @@ _execute_pointer_did_not_change: MOV sp, x4 // STR w3, [x2, x20, LSL #2] // Setup time-slice -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the thread entry function to indicate the thread is executing. */ diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_get.S index 066f5cb1..1ba20773 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_get.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_get.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,9 +58,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_core_get diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_preempt.S index 59c74b44..11945d9e 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_preempt.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -21,14 +21,17 @@ /**************************************************************************/ +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + .text .align 3 /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_core_preempt Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -62,9 +65,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_core_preempt diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_state_get.S index 23bea74d..a6b4a5cb 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_state_get.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,9 +58,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_current_state_get diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_thread_get.S index 43d7fb6e..b3f6f0af 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_thread_get.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,9 +58,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_current_thread_get diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_initialize_wait.S index adf62314..c806c02f 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_initialize_wait.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -60,9 +60,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_initialize_wait diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_low_level_initialize.S index 8f990ff4..ffb18050 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_low_level_initialize.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_low_level_initialize diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_protect.S index 92c1702b..9cde3e08 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_protect.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_protect.S @@ -32,7 +32,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,10 +64,10 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ /* improved SMP code, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_protect diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_time_get.S index 077ee82d..510ac91d 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_time_get.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_time_get.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_time_get diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_unprotect.S index ff8df7db..a783cde6 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_unprotect.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_smp_unprotect.S @@ -28,7 +28,7 @@ /* FUNCTION RELEASE */ /* */ /* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -61,9 +61,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_unprotect diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_stack_build.S index 5d50b6c7..73e79e79 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_stack_build.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_stack_build.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_stack_build Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -61,8 +61,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_system_return.S index 2d171af8..68c92a33 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_system_return.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_thread_system_return.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_system_return Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -61,9 +61,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_system_return(VOID) @@ -117,7 +117,7 @@ _skip_fp_save: MOV x1, #0 // Clear x1 STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#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. */ diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a35_smp/ac6/src/tx_timer_interrupt.S index 23f6b314..b73825b4 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_timer_interrupt.S +++ b/ports_smp/cortex_a35_smp/ac6/src/tx_timer_interrupt.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_timer_interrupt Cortex-A35-SMP/AC6 */ -/* 6.1.9 */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/GICv3_gicr.c index 56b91626..26b5af8a 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/GICv3_gicr.c +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -1,7 +1,7 @@ /* * GICv3_gicr.c - generic driver code for GICv3 redistributor * - * Copyright (c) 2014-2018 Arm Limited (or its affiliates). All rights reserved. + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. * Use, modification and redistribution of this file is subject to your possession of a * valid End User License Agreement for the Arm Product of which these examples are part of * and your compliance with all applicable terms and conditions of such licence agreement. @@ -129,6 +129,25 @@ static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) /**********************************************************************/ +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + void WakeupGICR(uint32_t gicr) { GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.S index 7b63558e..c787c3f5 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.S +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -1,7 +1,8 @@ // // Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A // -// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -43,6 +44,7 @@ _mutex_initialize: ret .cfi_endproc +#if !defined(USE_LSE_ATOMIC) .type _mutex_acquire, "function" .cfi_startproc @@ -84,3 +86,48 @@ _mutex_release: stlr w1, [x0] ret .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/sample_threadx.c index b766cf71..17cceb01 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/sample_threadx.c +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -3,19 +3,22 @@ byte pool, and block pool. */ #include "tx_api.h" -#include + extern void init_timer(void); /* in timer_interrupts.c */ -#define DEMO_STACK_SIZE 1024 -#define DEMO_BYTE_POOL_SIZE 0x20000 -#define DEMO_BLOCK_POOL_SIZE 100 -#define DEMO_QUEUE_SIZE 100 + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 -/* Define a memory area to create a byte pool in. */ +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + -UCHAR memory_area[DEMO_BYTE_POOL_SIZE] __attribute__((aligned (8))); /* Define the ThreadX object control blocks... */ @@ -68,6 +71,8 @@ UCHAR event_buffer[65536]; #endif +/* Define main entry point. */ + int main(void) { @@ -95,7 +100,7 @@ CHAR *pointer = TX_NULL; #endif /* 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); + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); /* Allocate the stack for thread 0. */ tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); @@ -386,4 +391,3 @@ UINT status; break; } } - diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/timer_interrupts.c index 7b0996ef..8f522217 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/timer_interrupts.c +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -2,10 +2,10 @@ /* Timer and interrupts */ -/* Copyright (c) 2016 Arm Limited (or its affiliates). All rights reserved. */ -/* Use, modification and redistribution of this file is subject to your */ -/* possession of a valid DS-5 end user licence agreement and your compliance */ -/* with all applicable terms and conditions of such licence agreement. */ +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ #include @@ -112,14 +112,14 @@ void fiqHandler(void) unsigned int aliased = 0; ID = getICC_IAR0(); // readIntAck(); - printf("fiqHandler() - Read %d from IAR0\n", ID); + //printf("fiqHandler() - Read %d from IAR0\n", ID); // Check for reserved IDs if ((1020 <= ID) && (ID <= 1023)) { - printf("fiqHandler() - Reserved INTID %d\n\n", ID); + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); ID = getICC_IAR1(); // readAliasedIntAck(); - printf("fiqHandler() - Read %d from AIAR\n", ID); + //printf("fiqHandler() - Read %d from AIAR\n", ID); aliased = 1; // If still spurious then simply return @@ -131,13 +131,13 @@ void fiqHandler(void) { case 34: // Dual-Timer 0 (SP804) - printf("fiqHandler() - External timer interrupt\n\n"); + //printf("fiqHandler() - External timer interrupt\n\n"); clearTimerIrq(); break; default: // Unexpected ID value - printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); break; } diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.S index f39eca6c..45445a98 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.S +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -1,7 +1,7 @@ // ------------------------------------------------------------ // Armv8-A AArch64 - Common helper functions // -// Copyright (c) 2012-2018 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -17,6 +17,7 @@ .global InvalidateUDCaches .global GetMIDR .global GetMPIDR + .global GetAffinity .global GetCPUID // ------------------------------------------------------------ @@ -138,12 +139,25 @@ GetMPIDR: .cfi_endproc + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + .type GetCPUID, "function" .cfi_startproc GetCPUID: mrs x0, MIDR_EL1 ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ cmp x0, #0xD0B // Cortex-A76 b.eq DynamIQ cmp x0, #0xD0A // Cortex-A75 @@ -158,6 +172,8 @@ DynamIQ: Others: mrs x0, MPIDR_EL1 - ubfx x0, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 ret .cfi_endproc diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_mmu.h index dbf903ca..bce62b54 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_mmu.h +++ b/ports_smp/cortex_a35_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -1,7 +1,7 @@ // // Defines for v8 Memory Model // -// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. // Use, modification and redistribution of this file is subject to your possession of a // valid End User License Agreement for the Arm Product of which these examples are part of // and your compliance with all applicable terms and conditions of such licence agreement. @@ -90,10 +90,20 @@ #define TT_S1_ATTR_AF (1 << 10) #define TT_S1_ATTR_nG (1 << 11) +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + #define TT_S1_ATTR_CONTIG (1 << 52) #define TT_S1_ATTR_PXN (1 << 53) #define TT_S1_ATTR_UXN (1 << 54) +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + #define TT_S1_MAIR_DEV_nGnRnE 0b00000000 #define TT_S1_MAIR_DEV_nGnRE 0b00000100 #define TT_S1_MAIR_DEV_nGRE 0b00001000 diff --git a/ports_smp/cortex_a35_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a35_smp/gnu/example_build/tx/.cproject index e8013d86..ec20edd2 100644 --- a/ports_smp/cortex_a35_smp/gnu/example_build/tx/.cproject +++ b/ports_smp/cortex_a35_smp/gnu/example_build/tx/.cproject @@ -131,7 +131,7 @@ - + diff --git a/ports_smp/cortex_a35_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a35_smp/gnu/inc/tx_port.h index 789408e3..fcac1485 100644 --- a/ports_smp/cortex_a35_smp/gnu/inc/tx_port.h +++ b/ports_smp/cortex_a35_smp/gnu/inc/tx_port.h @@ -25,8 +25,8 @@ /* */ /* PORT SPECIFIC C INFORMATION RELEASE */ /* */ -/* tx_port.h Cortex-A35-SMP/GNU */ -/* 6.1.6 */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ /* */ /* AUTHOR */ /* */ @@ -48,9 +48,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 04-02-2021 Bhupendra Naphade Modified comment(s),updated */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ /* macro definition, */ -/* resulting in version 6.1.6 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -405,8 +405,7 @@ typedef struct TX_THREAD_SMP_PROTECT_STRUCT #define TX_SEMAPHORE_DISABLE TX_DISABLE -/* Define VFP extension for the Cortex-A35. Each is assumed to be called in the context of the executing - thread. */ +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ #ifndef TX_SOURCE_CODE #define tx_thread_fp_enable _tx_thread_fp_enable @@ -421,7 +420,7 @@ VOID tx_thread_fp_disable(VOID); #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX Cortex-A35-SMP/GNU Version 6.1.9 *"; + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; #else extern CHAR _tx_version_id[]; #endif diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a35_smp/gnu/src/tx_initialize_low_level.S index 536fb516..0c377cac 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_initialize_low_level.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_initialize_low_level.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_initialize_low_level Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -62,8 +62,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_initialize_low_level(VOID) diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_restore.S index d6881f47..4df471ac 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_restore.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_restore.S @@ -30,8 +30,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_context_restore Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,9 +64,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_context_restore(VOID) @@ -79,7 +79,7 @@ _tx_thread_context_restore: MSR DAIFSet, 0x3 // Lockout interrupts -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the ISR exit function to indicate an ISR is complete. */ diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_save.S index 9e92cc98..79e33086 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_save.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_context_save.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_context_save Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -60,9 +60,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_context_save(VOID) @@ -135,7 +135,7 @@ _tx_thread_context_save: #endif STP x0, x1, [sp, #-16]! // Save SPSR, ELR -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the ISR enter function to indicate an ISR is executing. */ @@ -238,7 +238,7 @@ __tx_thread_idle_system_save: /* Not much to do here, just adjust the stack pointer, and return to IRQ processing. */ -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the ISR enter function to indicate an ISR is executing. */ diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_disable.c index 3fec9b82..3e5d7e21 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_disable.c +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_disable.c @@ -33,8 +33,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_fp_disable Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,8 +64,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_thread_fp_disable(VOID) diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_enable.c index a912d15a..4e69205c 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_enable.c +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_fp_enable.c @@ -32,8 +32,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_fp_enable Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -63,8 +63,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ VOID _tx_thread_fp_enable(VOID) diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_control.S index f15c8d68..6a5a7741 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_control.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_control.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_interrupt_control Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // UINT _tx_thread_interrupt_control(UINT new_posture) @@ -79,4 +79,3 @@ _tx_thread_interrupt_control: MOV x0, x1 // Setup return value RET // Return to caller // } - diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_disable.S index 36acefed..d0062ef8 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_disable.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_disable.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_interrupt_disable Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,8 +58,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // UINT _tx_thread_interrupt_disable(void) diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_restore.S index 8a1abb67..1b6261ba 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_restore.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_interrupt_restore.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_interrupt_restore Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // UINT _tx_thread_interrupt_restore(UINT old_posture) diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_schedule.S index 0ee24de8..35a8fc96 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_schedule.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_schedule.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_schedule Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -62,9 +62,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_schedule(VOID) @@ -205,7 +205,7 @@ _execute_pointer_did_not_change: MOV sp, x4 // STR w3, [x2, x20, LSL #2] // Setup time-slice -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) /* Call the thread entry function to indicate the thread is executing. */ diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_get.S index 654ac7e7..1ba20773 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_get.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_get.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_core_get Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,9 +58,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_core_get diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_preempt.S index c3eba08a..11945d9e 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_preempt.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -23,14 +23,15 @@ #define ICC_SGI1R_EL1 S3_0_C12_C11_5 + .text .align 3 /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_core_preempt Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,9 +65,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_core_preempt diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_state_get.S index 6f4328c7..a6b4a5cb 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_state_get.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_current_state_get Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,9 +58,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_current_state_get diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_thread_get.S index c7893892..b3f6f0af 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_thread_get.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -58,9 +58,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_current_thread_get diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_initialize_wait.S index 126f9e88..c806c02f 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_initialize_wait.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -60,9 +60,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_initialize_wait diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_low_level_initialize.S index fe6e2193..ffb18050 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_low_level_initialize.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_low_level_initialize diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_protect.S index 59bf4ff4..9cde3e08 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_protect.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_protect.S @@ -31,8 +31,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_protect Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -64,10 +64,10 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ /* improved SMP code, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_protect diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_time_get.S index edb09b03..510ac91d 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_time_get.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_time_get.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_time_get Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -59,8 +59,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_time_get diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_unprotect.S index c53075ae..a783cde6 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_unprotect.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_smp_unprotect.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_smp_unprotect Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -61,9 +61,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ .global _tx_thread_smp_unprotect diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_stack_build.S index 51013ede..73e79e79 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_stack_build.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_stack_build.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_stack_build Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -61,8 +61,8 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_system_return.S index aab24235..68c92a33 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_thread_system_return.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_thread_system_return.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_system_return Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -61,9 +61,9 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ +/* 01-31-2022 Andres Mlinar Updated comments, */ /* added ARMv8.2-A support, */ -/* resulting in version 6.1.9 */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ // VOID _tx_thread_system_return(VOID) @@ -117,7 +117,7 @@ _skip_fp_save: MOV x1, #0 // Clear x1 STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 -#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY +#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. */ diff --git a/ports_smp/cortex_a35_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a35_smp/gnu/src/tx_timer_interrupt.S index c9fd7235..b73825b4 100644 --- a/ports_smp/cortex_a35_smp/gnu/src/tx_timer_interrupt.S +++ b/ports_smp/cortex_a35_smp/gnu/src/tx_timer_interrupt.S @@ -27,8 +27,8 @@ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_timer_interrupt Cortex-A35-SMP/GCC */ -/* 6.1.9 */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ @@ -63,8 +63,6 @@ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Yuxin Zhou Modified comment(s), */ -/* resulting in version 6.1.9 */ /* */ /**************************************************************************/ // VOID _tx_timer_interrupt(VOID) diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..eb3c1a32 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..27743868 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a53_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..527407ae --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a53_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a53_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a53_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a53_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a53_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_module/cortex_a35_smp/gnu/module_manager/src/tx_thread_timeout.c b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_initialize_wait.S similarity index 51% rename from ports_module/cortex_a35_smp/gnu/module_manager/src/tx_thread_timeout.c rename to ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_initialize_wait.S index 00f48e14..c806c02f 100644 --- a/ports_module/cortex_a35_smp/gnu/module_manager/src/tx_thread_timeout.c +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -15,42 +15,33 @@ /** */ /** ThreadX Component */ /** */ -/** Thread */ +/** Thread - Low Level SMP Support */ /** */ /**************************************************************************/ /**************************************************************************/ -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - + .text + .align 3 /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_timeout Cortex-A35-SMP */ -/* 6.1 */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ /* */ /* INPUT */ /* */ -/* timeout_input Contains the thread pointer */ +/* None */ /* */ /* OUTPUT */ /* */ @@ -58,107 +49,91 @@ /* */ /* CALLS */ /* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ +/* _tx_thread_schedule Thread scheduling loop */ /* */ /* CALLED BY */ /* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ +/* Hardware */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: -TX_INTERRUPT_SAVE_AREA + /* Lockout interrupts. */ -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; + MSR DAIFSet, 0x3 // Lockout interrupts + /* Pickup the Core ID. */ - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID #else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID #endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID #endif - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ -#ifndef TX_NOT_INTERRUPTABLE + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else + /* Save the system stack pointer for this core. */ - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer -#ifndef TX_NOT_INTERRUPTABLE - /* Restore interrupts. */ - TX_RESTORE -#endif + /* Pickup the release cores flag. */ - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } + /* Core 0 has released this core. */ -#ifdef TX_NOT_INTERRUPTABLE + /* Clear this core's system state variable. */ - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a53_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a53_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a53_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..d0fd074c --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a53_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a53_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a53_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a53_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a53_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a53_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_timeout.c b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_initialize_wait.S similarity index 51% rename from ports_smp/cortex_a35_smp/ac6/src/tx_thread_timeout.c rename to ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_initialize_wait.S index 09be1086..c806c02f 100644 --- a/ports_smp/cortex_a35_smp/ac6/src/tx_thread_timeout.c +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -15,42 +15,33 @@ /** */ /** ThreadX Component */ /** */ -/** Thread */ +/** Thread - Low Level SMP Support */ /** */ /**************************************************************************/ /**************************************************************************/ -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - + .text + .align 3 /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ -/* _tx_thread_timeout Cortex-A35-SMP */ -/* 6.1.9 */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ /* */ /* INPUT */ /* */ -/* timeout_input Contains the thread pointer */ +/* None */ /* */ /* OUTPUT */ /* */ @@ -58,109 +49,91 @@ /* */ /* CALLS */ /* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ +/* _tx_thread_schedule Thread scheduling loop */ /* */ /* CALLED BY */ /* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ +/* Hardware */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: -TX_INTERRUPT_SAVE_AREA + /* Lockout interrupts. */ -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; + MSR DAIFSet, 0x3 // Lockout interrupts + /* Pickup the Core ID. */ - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID #else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID #endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID #endif - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ -#ifndef TX_NOT_INTERRUPTABLE + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else + /* Save the system stack pointer for this core. */ - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer -#ifndef TX_NOT_INTERRUPTABLE - /* Restore interrupts. */ - TX_RESTORE -#endif + /* Pickup the release cores flag. */ - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } + /* Core 0 has released this core. */ -#ifdef TX_NOT_INTERRUPTABLE + /* Clear this core's system state variable. */ - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a53_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a53_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a53_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..5c025c1c --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..f642a3e4 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a55_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..9cafc8db --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a55_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a55_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a55_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a55_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a55_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a55_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a55_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a55_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..2f22ba75 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a55_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a55_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a55_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a55_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a55_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a55_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a55_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a55_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a55_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..2f75cb03 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..674e9663 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a57_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..324a0057 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a57_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a57_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a57_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a57_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a57_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a57_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a57_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a57_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..17c737ee --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a57_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a57_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a57_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a57_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a57_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a57_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a57_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a57_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a57_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a5x_smp/ac6/src/tx_thread_timeout.c b/ports_smp/cortex_a5x_smp/ac6/src/tx_thread_timeout.c deleted file mode 100644 index d433dea6..00000000 --- a/ports_smp/cortex_a5x_smp/ac6/src/tx_thread_timeout.c +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) Microsoft Corporation. All rights reserved. */ -/* */ -/* This software is licensed under the Microsoft Software License */ -/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -/* and in the root directory of this software. */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_timeout Cortex-A5x-SMP */ -/* 6.1.9 */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* timeout_input Contains the thread pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ -/* */ -/**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ - -TX_INTERRUPT_SAVE_AREA - -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; - - - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE -#else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); -#endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; -#endif - - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; - -#ifndef TX_NOT_INTERRUPTABLE - - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else - - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif - -#ifndef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { - - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } - -#ifdef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} diff --git a/ports_smp/cortex_a5x_smp/gnu/src/tx_thread_timeout.c b/ports_smp/cortex_a5x_smp/gnu/src/tx_thread_timeout.c deleted file mode 100644 index d433dea6..00000000 --- a/ports_smp/cortex_a5x_smp/gnu/src/tx_thread_timeout.c +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) Microsoft Corporation. All rights reserved. */ -/* */ -/* This software is licensed under the Microsoft Software License */ -/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -/* and in the root directory of this software. */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_timeout Cortex-A5x-SMP */ -/* 6.1.9 */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* timeout_input Contains the thread pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* 10-15-2021 Andres Mlinar Updated comments, */ -/* resulting in version 6.1.9 */ -/* */ -/**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ - -TX_INTERRUPT_SAVE_AREA - -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; - - - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE -#else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); -#endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; -#endif - - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; - -#ifndef TX_NOT_INTERRUPTABLE - - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else - - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif - -#ifndef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { - - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } - -#ifdef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} diff --git a/ports_smp/cortex_a5x_smp/green/example_build/tx/libtx.gpj b/ports_smp/cortex_a5x_smp/green/example_build/tx/libtx.gpj index 6cdf4f5d..5abed507 100644 --- a/ports_smp/cortex_a5x_smp/green/example_build/tx/libtx.gpj +++ b/ports_smp/cortex_a5x_smp/green/example_build/tx/libtx.gpj @@ -39,7 +39,6 @@ ..\..\src\tx_thread_smp_unprotect.a64 ..\..\src\tx_thread_stack_build.a64 ..\..\src\tx_thread_system_return.a64 -..\..\src\tx_thread_timeout.c ..\..\src\tx_timer_interrupt.a64 ..\..\..\..\..\common_smp\src\tx_block_allocate.c ..\..\..\..\..\common_smp\src\tx_block_pool_cleanup.c @@ -233,3 +232,4 @@ ..\..\..\..\..\common_smp\src\txe_timer_deactivate.c ..\..\..\..\..\common_smp\src\txe_timer_delete.c ..\..\..\..\..\common_smp\src\txe_timer_info_get.c +..\..\..\..\..\common_smp\src\tx_thread_timeout.c diff --git a/ports_smp/cortex_a5x_smp/green/src/tx_thread_timeout.c b/ports_smp/cortex_a5x_smp/green/src/tx_thread_timeout.c deleted file mode 100644 index 22a22fe2..00000000 --- a/ports_smp/cortex_a5x_smp/green/src/tx_thread_timeout.c +++ /dev/null @@ -1,165 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) Microsoft Corporation. All rights reserved. */ -/* */ -/* This software is licensed under the Microsoft Software License */ -/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -/* and in the root directory of this software. */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_timeout Cortex-A5x-SMP */ -/* 6.1 */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* timeout_input Contains the thread pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 09-30-2020 William E. Lamie Initial Version 6.1 */ -/* */ -/**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ - -TX_INTERRUPT_SAVE_AREA - -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; - - - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE -#else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); -#endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; -#endif - - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; - -#ifndef TX_NOT_INTERRUPTABLE - - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else - - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif - -#ifndef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { - - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } - -#ifdef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} - diff --git a/ports_smp/cortex_a5x_smp/iar/src/tx_thread_timeout.c b/ports_smp/cortex_a5x_smp/iar/src/tx_thread_timeout.c deleted file mode 100644 index b0fa0a2f..00000000 --- a/ports_smp/cortex_a5x_smp/iar/src/tx_thread_timeout.c +++ /dev/null @@ -1,164 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright (c) Microsoft Corporation. All rights reserved. */ -/* */ -/* This software is licensed under the Microsoft Software License */ -/* Terms for Microsoft Azure RTOS. Full text of the license can be */ -/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ -/* and in the root directory of this software. */ -/* */ -/**************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Thread */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#define TX_SOURCE_CODE - - -/* Include necessary system files. */ - -#include "tx_api.h" -#include "tx_thread.h" -#include "tx_timer.h" - - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_thread_timeout Cortex-A5x-SMP */ -/* 6.1.9 */ -/* AUTHOR */ -/* */ -/* William E. Lamie, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function handles thread timeout processing. Timeouts occur in */ -/* two flavors, namely the thread sleep timeout and all other service */ -/* call timeouts. Thread sleep timeouts are processed locally, while */ -/* the others are processed by the appropriate suspension clean-up */ -/* service. */ -/* */ -/* INPUT */ -/* */ -/* timeout_input Contains the thread pointer */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* Suspension Cleanup Functions */ -/* _tx_thread_system_resume Resume thread */ -/* _tx_thread_system_ni_resume Non-interruptable resume thread */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_timer_expiration_process Timer expiration function */ -/* _tx_timer_thread_entry Timer thread function */ -/* */ -/* RELEASE HISTORY */ -/* */ -/* DATE NAME DESCRIPTION */ -/* */ -/* 10-15-2021 William E. Lamie Initial Version 6.1.9 */ -/* */ -/**************************************************************************/ -VOID _tx_thread_timeout(ULONG timeout_input) -{ - -TX_INTERRUPT_SAVE_AREA - -TX_THREAD *thread_ptr; -VOID (*suspend_cleanup)(struct TX_THREAD_STRUCT *suspend_thread_ptr, ULONG suspension_sequence); -ULONG suspension_sequence; - - - /* Pickup the thread pointer. */ - TX_THREAD_TIMEOUT_POINTER_SETUP(thread_ptr) - - /* Disable interrupts. */ - TX_DISABLE - - /* Determine how the thread is currently suspended. */ - if (thread_ptr -> tx_thread_state == TX_SLEEP) - { - -#ifdef TX_NOT_INTERRUPTABLE - - /* Resume the thread! */ - _tx_thread_system_ni_resume(thread_ptr); - - /* Restore interrupts. */ - TX_RESTORE -#else - - /* Increment the disable preemption flag. */ - _tx_thread_preempt_disable++; - - /* Restore interrupts. */ - TX_RESTORE - - /* Lift the suspension on the sleeping thread. */ - _tx_thread_system_resume(thread_ptr); -#endif - } - else - { - - /* Process all other suspension timeouts. */ - -#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO - - /* Increment the total number of thread timeouts. */ - _tx_thread_performance_timeout_count++; - - /* Increment the number of timeouts for this thread. */ - thread_ptr -> tx_thread_performance_timeout_count++; -#endif - - /* Pickup the cleanup routine address. */ - suspend_cleanup = thread_ptr -> tx_thread_suspend_cleanup; - -#ifndef TX_NOT_INTERRUPTABLE - - /* Pickup the suspension sequence number that is used later to verify that the - cleanup is still necessary. */ - suspension_sequence = thread_ptr -> tx_thread_suspension_sequence; -#else - - /* When not interruptable is selected, the suspension sequence is not used - just set to 0. */ - suspension_sequence = ((ULONG) 0); -#endif - -#ifndef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - - /* Call any cleanup routines. */ - if (suspend_cleanup != TX_NULL) - { - - /* Yes, there is a function to call. */ - (suspend_cleanup)(thread_ptr, suspension_sequence); - } - -#ifdef TX_NOT_INTERRUPTABLE - - /* Restore interrupts. */ - TX_RESTORE -#endif - } -} diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..3bccbdce --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..35b90705 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a65_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..0d5e8570 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a65_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a65_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a65_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a65_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a65_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a65_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a65_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..bc7dcad0 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a65_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a65_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a65_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a65_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a65_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a65_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a65_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a65_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..12662334 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..09d5f09b --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..f82c4a12 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a65ae_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a65ae_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a65ae_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a65ae_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..0ffed216 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a65ae_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a65ae_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a65ae_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a65ae_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a65ae_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..188d23b9 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..354f9ea7 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a72_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..e944fcf6 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a72_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a72_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a72_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a72_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a72_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a72_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a72_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a72_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..6a746fbc --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a72_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a72_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a72_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a72_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a72_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a72_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a72_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a72_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a72_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..eb56d9c0 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..d9e3d93f --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a73_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..dd9e40a1 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a73_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a73_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a73_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a73_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a73_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a73_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a73_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a73_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..1cc749ae --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a73_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a73_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a73_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a73_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a73_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a73_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a73_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a73_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a73_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..55342a4b --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..28c5aab8 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a75_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..75bd3a7c --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a75_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a75_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a75_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a75_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a75_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a75_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a75_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a75_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..76a6ad45 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a75_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a75_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a75_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a75_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a75_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a75_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a75_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a75_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a75_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..590d7745 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..a665e8c3 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a76_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..33721eb4 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a76_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a76_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a76_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a76_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a76_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a76_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a76_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..58967dca --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a76_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a76_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a76_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a76_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a76_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a76_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a76_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a76_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..4a1565ae --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..f261e124 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..b662b32b --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a76ae_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a76ae_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a76ae_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a76ae_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..8108d076 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a76ae_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a76ae_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a76ae_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a76ae_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a76ae_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..552e0f6e --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..8d2fe10c --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,413 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a77_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..7dd056a4 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a77_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a77_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a77_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a77_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a77_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a77_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a77_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a77_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..a7f12edc --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a77_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..ec20edd2 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a77_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a77_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a77_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a77_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a77_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..1b6261ba --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..11945d9e --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,86 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a77_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a77_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a77_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.cproject b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..f79ffe9f --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.cproject @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.project b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..2cf1553b --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +static GICv3_distributor __attribute__((section(".bss.distributor"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..d91aeb27 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".bss.redistributor"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..9aefbdcd --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,409 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.scat b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.scat new file mode 100644 index 00000000..1288a328 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sample_threadx.scat @@ -0,0 +1,262 @@ +;******************************************************** +; Scatter file for Armv8-A Startup code on FVP Base model +; Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +; Use, modification and redistribution of this file is subject to your +; possession of a valid DS-5 end user licence agreement and your compliance +; with all applicable terms and conditions of such licence agreement. +;******************************************************** + +LOAD 0x80000000 +{ + EXEC +0 + { + startup.o (StartUp, +FIRST) + * (+RO, +RW, +ZI) + + } + ;XTABLES +0 ALIGN 0x1000 + ;{ + ; xtables.o (*) + ;} + + SYS_STACK +0 ALIGN 8 EMPTY 0x1000 + { + } + + ; + ; Separate heap - import symbol __use_two_region_memory + ; in source code for this to work correctly + ; + ARM_LIB_HEAP +0 ALIGN 64 EMPTY 0xA0000 {} + + ; + ; App stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + ARM_LIB_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Handler stacks for all CPUs + ; All stacks and heap are aligned to a cache-line boundary + ; + HANDLER_STACK +0 ALIGN 64 EMPTY 4 * 0x4000 {} + + ; + ; Stacks for EL3 + ; + EL3_STACKS +0 ALIGN 64 EMPTY 4 * 0x1000 {} + ; + ; Strictly speaking, the L1 tables don't need to + ; be so strongly aligned, but no matter + ; + TTB0_L1 +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; Various sets of L2 tables + ; + ; Alignment is 4KB, since the code uses a 4K page + ; granularity - larger granularities would require + ; correspondingly stricter alignment + ; + TTB0_L2_RAM +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PRIVATE +0 ALIGN 4096 EMPTY 0x1000 {} + + TTB0_L2_PERIPH +0 ALIGN 4096 EMPTY 0x1000 {} + + ; + ; The startup code uses the end of this region to calculate + ; the top of memory - don't place any RAM regions after it + ; + TOP_OF_RAM +0 EMPTY 4 {} + + ; + ; CS3 Peripherals is a 64MB region from 0x1c000000 + ; that includes the following: + ; System Registers at 0x1C010000 + ; UART0 (PL011) at 0x1C090000 + ; Color LCD Controller (PL111) at 0x1C1F0000 + ; plus a number of others. + ; CS3_PERIPHERALS is used by the startup code for page-table generation + ; This region is not truly empty, but we have no + ; predefined objects that live within it + ; + CS3_PERIPHERALS 0x1c000000 EMPTY 0x90000 {} + + ; + ; Place the UART peripheral registers data structure + ; This is only really needed if USE_SERIAL_PORT is defined, but + ; the linker will remove unused sections if not needed +; PL011 0x1c090000 UNINIT 0x1000 +; { +; uart.o (+ZI) +; } + ; Note that some other CS3_PERIPHERALS follow this + + ; + ; GICv3 distributor + ; + GICD 0x2f000000 UNINIT 0x8000 + { + GICv3_gicd.o (.bss.distributor) + } + + ; + ; GICv3 redistributors + ; 128KB for each redistributor in the system + ; + GICR 0x2f100000 UNINIT 0x80000 + { + GICv3_gicr.o (.bss.redistributor) + } +} + + + + + +; Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; Redistributions of source code must retain the above copyright notice, this +; list of conditions and the following disclaimer. +; +; Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; +; Neither the name of ARM nor the names of its contributors may be used +; to endorse or promote products derived from this software without specific +; prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +; POSSIBILITY OF SUCH DAMAGE. + +;RO AlignExpr(0x80000000, 0x1000) +;{ +; C_SYNCH_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_sp0) +; } +; C_IRQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_sp0) +; } +; C_FIQ_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_sp0) +; } +; C_SERROR_SP0 +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_sp0) +; } +; +; C_SYNCH_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_synch_spx) +; } +; C_IRQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_irq_spx) +; } +; C_FIQ_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_fiq_spx) +; } +; C_SERROR_SPX +0 ALIGN 0x80 +; { +; vectors.o (_c_serror_spx) +; } +; +; L64_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l64_synch) +; } +; L64_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_irq) +; } +; L64_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l64_fiq) +; } +; L64_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l64_serror) +; } +; +; L32_SYNCH +0 ALIGN 0x80 +; { +; vectors.o (_l32_synch) +; } +; L32_IRQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_irq) +; } +; L32_FIQ +0 ALIGN 0x80 +; { +; vectors.o (_l32_fiq) +; } +; L32_SERROR +0 ALIGN 0x80 +; { +; vectors.o (_l32_serror) +; } +; +; ROSEC +0 ALIGN 8 +; { +; *( +RO ) +; } +; +; XTABLES +0 ALIGN 0x1000 +; { +; xtables.o (*) +; } +;} +; +;RW AlignExpr(ImageLimit(XTABLES), 0x1000) +;{ +; RWSEC +0 +; { +; *( +RW ) +; } +; +; ZISEC +0 +; { +; *( +ZI ) +; } +; +; SYS_STACK +0 ALIGN 8 EMPTY 0x1000 +; { +; } +; +; ; 512 MiB heap +; HEAP +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Free Memory section +; FREE_MEMORY +0 ALIGN 8 EMPTY 0x10000000 +; { +; } +; +; ; Per-CPU 128 MiB task stacks +; TASK_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +; +; ; Per-CPU 128 MiB handler stacks +; HANDLER_STACKS +0 ALIGN 16 EMPTY (4 * 0x8000000) +; { +; } +;} diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/startup.S b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..ee87d101 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/startup.S @@ -0,0 +1,803 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global GetGICR + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global __main + + .global Image$$EXEC$$RO$$Base + .global Image$$TTB0_L1$$ZI$$Base + .global Image$$TTB0_L2_RAM$$ZI$$Base + .global Image$$TTB0_L2_PERIPH$$ZI$$Base + .global Image$$TOP_OF_RAM$$ZI$$Base + .global Image$$GICD$$ZI$$Base + .global Image$$ARM_LIB_STACK$$ZI$$Limit + .global Image$$EL3_STACKS$$ZI$$Limit + .global Image$$CS3_PERIPHERALS$$ZI$$Base + // use separate stack and heap, as anticipated by scatter.scat + .global __use_two_region_memory + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =Image$$EL3_STACKS$$ZI$$Limit + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =Image$$HANDLER_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =Image$$ARM_LIB_STACK$$ZI$$Limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =Image$$TTB0_L1$$ZI$$Base + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =Image$$TTB0_L1$$ZI$$Base + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =Image$$TTB0_L2_RAM$$ZI$$Base + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =Image$$EXEC$$RO$$Base + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =Image$$TOP_OF_RAM$$ZI$$Base + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =Image$$TTB0_L2_PERIPH$$ZI$$Base + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =Image$$GICD$$ZI$$Base + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =Image$$CS3_PERIPHERALS$$ZI$$Base + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + ldr x1, =(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // Branch to C library init code + // + b __main + + +// ------------------------------------------------------------ + +// AArch64 Arm C library startup add-in: + +// The Arm Architecture Reference Manual for Armv8-A states: +// +// Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. +// Correspondingly, the sequence for ensuring that modifications to instructions are available +// for execution must include invalidation of the modified locations from the instruction cache, +// even if the instructions are held in Normal Non-cacheable memory. +// This includes cases where the instruction cache is disabled. +// +// To invalidate the AArch64 instruction cache after scatter-loading and before initialization of the stack and heap, +// it is necessary for the user to: +// +// * Implement instruction cache invalidation code in _platform_pre_stackheap_init. +// * Ensure all code on the path from the program entry up to and including _platform_pre_stackheap_init is located in a root region. +// +// In this example, this function is only called once, by the primary core + + .global _platform_pre_stackheap_init + .type _platform_pre_stackheap_init, "function" + .cfi_startproc +_platform_pre_stackheap_init: + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + ret + .cfi_endproc + + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + // B MainApp + + /** + * Retargeted to prevent the C library from doing its own stack and heap + * initialisation. + */ + .type __user_setup_stackheap, @function +__user_setup_stackheap: + ADRP X0, Image$$SYS_STACK$$ZI$$Limit + MOV SP, X0 + ADRP X0, Image$$ARM_LIB_HEAP$$ZI$$Base + ADRP X2, Image$$ARM_LIB_HEAP$$ZI$$Limit + RET + diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/tx/.cproject b/ports_smp/cortex_a78_smp/ac6/example_build/tx/.cproject new file mode 100644 index 00000000..2eecb412 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/tx/.cproject @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a78_smp/ac6/example_build/tx/.project b/ports_smp/cortex_a78_smp/ac6/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a78_smp/ac6/inc/tx_port.h b/ports_smp/cortex_a78_smp/ac6/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_initialize_low_level.S b/ports_smp/cortex_a78_smp/ac6/src/tx_initialize_low_level.S new file mode 100644 index 00000000..59fe38e0 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_initialize_low_level.S @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) Image$$HEAP$$ZI$$Limit; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =heap_limit // Pickup unused memory address - A free + LDR x1, [x1] // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 +heap_limit: + .quad (Image$$TOP_OF_RAM$$ZI$$Limit) + diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_restore.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_save.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_disable.c b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_enable.c b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..d4507819 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_schedule.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..4feeca33 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protect.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_stack_build.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_thread_system_return.S b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a78_smp/ac6/src/tx_timer_interrupt.S b/ports_smp/cortex_a78_smp/ac6/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a78_smp/ac6/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.cproject b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.cproject new file mode 100644 index 00000000..34967225 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.cproject @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.project b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.project new file mode 100644 index 00000000..a1b15572 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/.project @@ -0,0 +1,26 @@ + + + sample_threadx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3.h new file mode 100644 index 00000000..dfe37586 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3.h @@ -0,0 +1,561 @@ +/* + * GICv3.h - data types and function prototypes for GICv3 utility routines + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_h +#define GICV3_h + +#include + +/* + * extra flags for GICD enable + */ +typedef enum +{ + gicdctlr_EnableGrp0 = (1 << 0), + gicdctlr_EnableGrp1NS = (1 << 1), + gicdctlr_EnableGrp1A = (1 << 1), + gicdctlr_EnableGrp1S = (1 << 2), + gicdctlr_EnableAll = (1 << 2) | (1 << 1) | (1 << 0), + gicdctlr_ARE_S = (1 << 4), /* Enable Secure state affinity routing */ + gicdctlr_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + gicdctlr_DS = (1 << 6), /* Disable Security support */ + gicdctlr_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICDCTLRFlags_t; + +/* + * modes for SPI routing + */ +typedef enum +{ + gicdirouter_ModeSpecific = 0, + gicdirouter_ModeAny = (1 << 31) +} GICDIROUTERBits_t; + +typedef enum +{ + gicdicfgr_Level = 0, + gicdicfgr_Edge = (1 << 1) +} GICDICFGRBits_t; + +typedef enum +{ + gicigroupr_G0S = 0, + gicigroupr_G1NS = (1 << 0), + gicigroupr_G1S = (1 << 2) +} GICIGROUPRBits_t; + +typedef enum +{ + gicrwaker_ProcessorSleep = (1 << 1), + gicrwaker_ChildrenAsleep = (1 << 2) +} GICRWAKERBits_t; + +/**********************************************************************/ + +/* + * Utility macros & functions + */ +#define RANGE_LIMIT(x) ((sizeof(x) / sizeof((x)[0])) - 1) + +static inline uint64_t gicv3PackAffinity(uint32_t aff3, uint32_t aff2, + uint32_t aff1, uint32_t aff0) +{ + /* + * only need to cast aff3 to get type promotion for all affinities + */ + return ((((uint64_t)aff3 & 0xff) << 32) | + ((aff2 & 0xff) << 16) | + ((aff1 & 0xff) << 8) | aff0); +} + +/**********************************************************************/ + +/* + * GIC Distributor Function Prototypes + */ + +/* + * ConfigGICD - configure GIC Distributor prior to enabling it + * + * Inputs: + * + * control - control flags + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void ConfigGICD(GICDCTLRFlags_t flags); + +/* + * EnableGICD - top-level enable for GIC Distributor + * + * Inputs: + * + * flags - new control flags to set + * + * Returns: + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void EnableGICD(GICDCTLRFlags_t flags); + +/* + * DisableGICD - top-level disable for GIC Distributor + * + * Inputs + * + * flags - control flags to clear + * + * Returns + * + * + * + * NOTE: + * + * ConfigGICD() will set an absolute flags value, whereas + * {En,Dis}ableGICD() will only {set,clear} the flag bits + * passed as a parameter + */ +void DisableGICD(GICDCTLRFlags_t flags); + +/* + * SyncAREinGICD - synchronise GICD Address Routing Enable bits + * + * Inputs + * + * flags - absolute flag bits to set in GIC Distributor + * + * dosync - flag whether to wait for ARE bits to match passed + * flag field (dosync = true), or whether to set absolute + * flag bits (dosync = false) + * + * Returns + * + * + * + * NOTE: + * + * This function is used to resolve a race in an MP system whereby secondary + * CPUs cannot reliably program all Redistributor registers until the + * primary CPU has enabled Address Routing. The primary CPU will call this + * function with dosync = false, while the secondaries will call it with + * dosync = true. + */ +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync); + +/* + * EnableSPI - enable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnableSPI(uint32_t id); + +/* + * DisableSPI - disable a specific shared peripheral interrupt + * + * Inputs: + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisableSPI(uint32_t id); + +/* + * SetSPIPriority - configure the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetSPIPriority(uint32_t id, uint32_t priority); + +/* + * GetSPIPriority - determine the priority for a shared peripheral interrupt + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * interrupt priority in the range 0 - 0xff + */ +uint32_t GetSPIPriority(uint32_t id); + +/* + * SetSPIRoute - specify interrupt routing when gicdctlr_ARE is enabled + * + * Inputs: + * + * id - interrupt identifier + * + * affinity - prepacked "dotted quad" affinity routing. NOTE: use the + * gicv3PackAffinity() helper routine to generate this input + * + * mode - select routing mode (specific affinity, or any recipient) + * + * Returns: + * + * + */ +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode); + +/* + * GetSPIRoute - read ARE-enabled interrupt routing information + * + * Inputs: + * + * id - interrupt identifier + * + * Returns: + * + * routing configuration + */ +uint64_t GetSPIRoute(uint32_t id); + +/* + * SetSPITarget - configure the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * target - 8-bit target bitmap + * + * Returns + * + * + */ +void SetSPITarget(uint32_t id, uint32_t target); + +/* + * GetSPITarget - read the set of processor targets for an interrupt + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * 8-bit target bitmap + */ +uint32_t GetSPITarget(uint32_t id); + +/* + * ConfigureSPI - setup an interrupt as edge- or level-triggered + * + * Inputs + * + * id - interrupt identifier + * + * config - desired configuration + * + * Returns + * + * + */ +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config); + +/* + * SetSPIPending - mark an interrupt as pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetSPIPending(uint32_t id); + +/* + * ClearSPIPending - mark an interrupt as not pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearSPIPending(uint32_t id); + +/* + * GetSPIPending - query whether an interrupt is pending + * + * Inputs + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetSPIPending(uint32_t id); + +/* + * SetSPISecurity - mark a shared peripheral interrupt as + * security + * + * Inputs + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group); + +/* + * SetSPISecurityBlock - mark a block of 32 shared peripheral + * interrupts as security + * + * Inputs: + * + * block - which block to mark (e.g. 1 = Ints 32-63) + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group); + +/* + * SetSPISecurityAll - mark all shared peripheral interrupts + * as security + * + * Inputs: + * + * group - the group for the interrupts + * + * Returns: + * + * + */ +void SetSPISecurityAll(GICIGROUPRBits_t group); + +/**********************************************************************/ + +/* + * GIC Re-Distributor Function Prototypes + * + * The model for calling Redistributor functions is that, rather than + * identifying the target redistributor with every function call, the + * SelectRedistributor() function is used to identify which redistributor + * is to be used for all functions until a different redistributor is + * explicitly selected + */ + +/* + * WakeupGICR - wake up a Redistributor + * + * Inputs: + * + * gicr - which Redistributor to wakeup + * + * Returns: + * + * + */ +void WakeupGICR(uint32_t gicr); + +/* + * EnablePrivateInt - enable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to enable + * + * Returns: + * + * + */ +void EnablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * DisablePrivateInt - disable a private (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - which interrupt to disable + * + * Returns: + * + * + */ +void DisablePrivateInt(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * priority - 8-bit priority to program (see note below) + * + * Returns: + * + * + * + * Note: + * + * The GICv3 architecture makes this function sensitive to the Security + * context in terms of what effect it has on the programmed priority: no + * attempt is made to adjust for the reduced priority range available + * when making Non-Secure accesses to the GIC + */ +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority); + +/* + * GetPrivateIntPriority - configure the priority for a private + * (SGI/PPI) interrupt + * + * Inputs: + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns: + * + * Int priority + */ +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntPending - mark a private (SGI/PPI) interrupt as pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void SetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * ClearPrivateIntPending - mark a private (SGI/PPI) interrupt as not pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * + */ +void ClearPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * GetPrivateIntPending - query whether a private (SGI/PPI) interrupt is pending + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - interrupt identifier + * + * Returns + * + * pending status + */ +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id); + +/* + * SetPrivateIntSecurity - mark a private (SGI/PPI) interrupt as + * security + * + * Inputs + * + * gicr - which Redistributor to program + * + * id - which interrupt to mark + * + * group - the group for the interrupt + * + * Returns + * + * + */ +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group); + +/* + * SetPrivateIntSecurityBlock - mark all 32 private (SGI/PPI) + * interrupts as security + * + * Inputs: + * + * gicr - which Redistributor to program + * + * group - the group for the interrupt + * + * Returns: + * + * + */ +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group); + +#endif /* ndef GICV3_h */ + +/* EOF GICv3.h */ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_aliases.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_aliases.h new file mode 100644 index 00000000..826ba973 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_aliases.h @@ -0,0 +1,113 @@ +// +// Aliases for GICv3 registers +// +// Copyright (c) 2016-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef GICV3_ALIASES_H +#define GICV3_ALIASES_H + +#ifndef __clang__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * Arm Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#endif /* not __clang__ */ + +#endif /* GICV3_ALIASES */ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicc.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicc.h new file mode 100644 index 00000000..998d92b5 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicc.h @@ -0,0 +1,254 @@ +/* + * GICv3_gicc.h - prototypes and inline functions for GICC system register operations + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef GICV3_gicc_h +#define GICV3_gicc_h + +#include "GICv3_aliases.h" + +#define stringify_no_expansion(x) #x +#define stringify(x) stringify_no_expansion(x) + +/**********************************************************************/ + +typedef enum +{ + sreSRE = (1 << 0), + sreDFB = (1 << 1), + sreDIB = (1 << 2), + sreEnable = (1 << 3) +} ICC_SREBits_t; + +static inline void setICC_SRE_EL1(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL2(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL2)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL2(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL2)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_SRE_EL3(ICC_SREBits_t mode) +{ + asm("msr "stringify(ICC_SRE_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_SRE_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_SRE_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + igrpEnable = (1 << 0), + igrpEnableGrp1NS = (1 << 0), + igrpEnableGrp1S = (1 << 2) +} ICC_IGRPBits_t; + +static inline void setICC_IGRPEN0_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN0_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL1(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline void setICC_IGRPEN1_EL3(ICC_IGRPBits_t mode) +{ + asm("msr "stringify(ICC_IGRPEN1_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +/**********************************************************************/ + +typedef enum +{ + ctlrCBPR = (1 << 0), + ctlrCBPR_EL1S = (1 << 0), + ctlrEOImode = (1 << 1), + ctlrCBPR_EL1NS = (1 << 1), + ctlrEOImode_EL3 = (1 << 2), + ctlrEOImode_EL1S = (1 << 3), + ctlrEOImode_EL1NS = (1 << 4), + ctlrRM = (1 << 5), + ctlrPMHE = (1 << 6) +} ICC_CTLRBits_t; + +static inline void setICC_CTLR_EL1(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL1)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_CTLR_EL3(ICC_CTLRBits_t mode) +{ + asm("msr "stringify(ICC_CTLR_EL3)", %0\n; isb" :: "r" ((uint64_t)mode)); +} + +static inline uint64_t getICC_CTLR_EL3(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_CTLR_EL3)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +static inline uint64_t getICC_IAR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_IAR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_IAR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline void setICC_EOIR0(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR0_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_EOIR1(uint32_t interrupt) +{ + asm("msr "stringify(ICC_EOIR1_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_DIR(uint32_t interrupt) +{ + asm("msr "stringify(ICC_DIR_EL1)", %0\n; isb" :: "r" ((uint64_t)interrupt)); +} + +static inline void setICC_PMR(uint32_t priority) +{ + asm("msr "stringify(ICC_PMR_EL1)", %0\n; isb" :: "r" ((uint64_t)priority)); +} + +static inline void setICC_BPR0(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR0_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline void setICC_BPR1(uint32_t binarypoint) +{ + asm("msr "stringify(ICC_BPR1_EL1)", %0\n; isb" :: "r" ((uint64_t)binarypoint)); +} + +static inline uint64_t getICC_BPR0(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR0_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_BPR1(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_BPR1_EL1)"\n" : "=r" (retc)); + + return retc; +} + +static inline uint64_t getICC_RPR(void) +{ + uint64_t retc; + + asm("mrs %0, "stringify(ICC_RPR_EL1)"\n" : "=r" (retc)); + + return retc; +} + +/**********************************************************************/ + +typedef enum +{ + sgirIRMTarget = 0, + sgirIRMAll = (1ull << 40) +} ICC_SGIRBits_t; + +static inline void setICC_SGI0R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI0R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_SGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_SGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +static inline void setICC_ASGI1R(uint8_t aff3, uint8_t aff2, + uint8_t aff1, ICC_SGIRBits_t irm, + uint16_t targetlist, uint8_t intid) +{ + uint64_t packedbits = (((uint64_t)aff3 << 48) | ((uint64_t)aff2 << 32) | \ + ((uint64_t)aff1 << 16) | irm | targetlist | \ + ((uint64_t)(intid & 0x0f) << 24)); + + asm("msr "stringify(ICC_ASGI1R_EL1)", %0\n; isb" :: "r" (packedbits)); +} + +#endif /* ndef GICV3_gicc_h */ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicd.c b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicd.c new file mode 100644 index 00000000..464ecced --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicd.c @@ -0,0 +1,339 @@ +/* + * GICv3_gicd.c - generic driver code for GICv3 distributor + * + * Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include + +#include "GICv3.h" + +typedef struct +{ + volatile uint32_t GICD_CTLR; // +0x0000 + const volatile uint32_t GICD_TYPER; // +0x0004 + const volatile uint32_t GICD_IIDR; // +0x0008 + + const volatile uint32_t padding0; // +0x000c + + volatile uint32_t GICD_STATUSR; // +0x0010 + + const volatile uint32_t padding1[3]; // +0x0014 + + volatile uint32_t IMP_DEF[8]; // +0x0020 + + volatile uint32_t GICD_SETSPI_NSR; // +0x0040 + const volatile uint32_t padding2; // +0x0044 + volatile uint32_t GICD_CLRSPI_NSR; // +0x0048 + const volatile uint32_t padding3; // +0x004c + volatile uint32_t GICD_SETSPI_SR; // +0x0050 + const volatile uint32_t padding4; // +0x0054 + volatile uint32_t GICD_CLRSPI_SR; // +0x0058 + + const volatile uint32_t padding5[3]; // +0x005c + + volatile uint32_t GICD_SEIR; // +0x0068 + + const volatile uint32_t padding6[5]; // +0x006c + + volatile uint32_t GICD_IGROUPR[32]; // +0x0080 + + volatile uint32_t GICD_ISENABLER[32]; // +0x0100 + volatile uint32_t GICD_ICENABLER[32]; // +0x0180 + volatile uint32_t GICD_ISPENDR[32]; // +0x0200 + volatile uint32_t GICD_ICPENDR[32]; // +0x0280 + volatile uint32_t GICD_ISACTIVER[32]; // +0x0300 + volatile uint32_t GICD_ICACTIVER[32]; // +0x0380 + + volatile uint8_t GICD_IPRIORITYR[1024]; // +0x0400 + volatile uint8_t GICD_ITARGETSR[1024]; // +0x0800 + volatile uint32_t GICD_ICFGR[64]; // +0x0c00 + volatile uint32_t GICD_IGRPMODR[32]; // +0x0d00 + const volatile uint32_t padding7[32]; // +0x0d80 + volatile uint32_t GICD_NSACR[64]; // +0x0e00 + + volatile uint32_t GICD_SGIR; // +0x0f00 + + const volatile uint32_t padding8[3]; // +0x0f04 + + volatile uint32_t GICD_CPENDSGIR[4]; // +0x0f10 + volatile uint32_t GICD_SPENDSGIR[4]; // +0x0f20 + + const volatile uint32_t padding9[52]; // +0x0f30 + const volatile uint32_t padding10[5120]; // +0x1000 + + volatile uint64_t GICD_IROUTER[1024]; // +0x6000 +} GICv3_distributor; + +/* + * use the scatter file to place GICD + */ +GICv3_distributor __attribute__((section(".gicd"))) gicd; + +void ConfigGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR = flags; +} + +void EnableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR |= flags; +} + +void DisableGICD(GICDCTLRFlags_t flags) +{ + gicd.GICD_CTLR &= ~flags; +} + +void SyncAREinGICD(GICDCTLRFlags_t flags, uint32_t dosync) +{ + if (dosync) + { + const uint32_t tmask = gicdctlr_ARE_S | gicdctlr_ARE_NS; + const uint32_t tval = flags & tmask; + + while ((gicd.GICD_CTLR & tmask) != tval) + continue; + } + else + gicd.GICD_CTLR = flags; +} + +void EnableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISENABLER); + id &= 32 - 1; + + gicd.GICD_ISENABLER[bank] = 1 << id; + + return; +} + +void DisableSPI(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISENABLER has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICENABLER); + id &= 32 - 1; + + gicd.GICD_ICENABLER[bank] = 1 << id; + + return; +} + +void SetSPIPriority(uint32_t id, uint32_t priority) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + gicd.GICD_IPRIORITYR[bank] = priority; +} + +uint32_t GetSPIPriority(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IPRIORITYR); + + return (uint32_t)(gicd.GICD_IPRIORITYR[bank]); +} + +void SetSPIRoute(uint32_t id, uint64_t affinity, GICDIROUTERBits_t mode) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + gicd.GICD_IROUTER[bank] = affinity | (uint64_t)mode; +} + +uint64_t GetSPIRoute(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_IROUTER has one doubleword-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_IROUTER); + + return gicd.GICD_IROUTER[bank]; +} + +void SetSPITarget(uint32_t id, uint32_t target) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + gicd.GICD_ITARGETSR[bank] = target; +} + +uint32_t GetSPITarget(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ITARGETSR has one byte-wide entry per interrupt + */ + /* + * GICD_ITARGETSR has 4 interrupts per register, i.e. 8-bits of + * target bitmap per register + */ + bank = id & RANGE_LIMIT(gicd.GICD_ITARGETSR); + + return (uint32_t)(gicd.GICD_ITARGETSR[bank]); +} + +void ConfigureSPI(uint32_t id, GICDICFGRBits_t config) +{ + uint32_t bank, tmp; + + /* + * GICD_ICFGR has 16 interrupts per register, i.e. 2-bits of + * configuration per register + */ + bank = (id >> 4) & RANGE_LIMIT(gicd.GICD_ICFGR); + config &= 3; + + id = (id & 0xf) << 1; + + tmp = gicd.GICD_ICFGR[bank]; + tmp &= ~(3 << id); + tmp |= config << id; + gicd.GICD_ICFGR[bank] = tmp; +} + +void SetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ISPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ISPENDR); + id &= 0x1f; + + gicd.GICD_ISPENDR[bank] = 1 << id; +} + +void ClearSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + gicd.GICD_ICPENDR[bank] = 1 << id; +} + +uint32_t GetSPIPending(uint32_t id) +{ + uint32_t bank; + + /* + * GICD_ICPENDR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_ICPENDR); + id &= 0x1f; + + return (gicd.GICD_ICPENDR[bank] >> id) & 1; +} + +void SetSPISecurity(uint32_t id, GICIGROUPRBits_t group) +{ + uint32_t bank, groupmod; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + bank = (id >> 5) & RANGE_LIMIT(gicd.GICD_IGROUPR); + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicd.GICD_IGROUPR[bank] |= 1 << id; + else + gicd.GICD_IGROUPR[bank] &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicd.GICD_IGRPMODR[bank] |= 1 << id; + else + gicd.GICD_IGRPMODR[bank] &= ~(1 << id); +} + +void SetSPISecurityBlock(uint32_t block, GICIGROUPRBits_t group) +{ + uint32_t groupmod; + const uint32_t nbits = (sizeof group * 8) - 1; + + /* + * GICD_IGROUPR has 32 interrupts per register + */ + block &= RANGE_LIMIT(gicd.GICD_IGROUPR); + + /* + * get each bit of group config duplicated over all 32-bits in a word + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicd.GICD_IGROUPR[block] = group; + gicd.GICD_IGRPMODR[block] = groupmod; +} + +void SetSPISecurityAll(GICIGROUPRBits_t group) +{ + uint32_t block; + + /* + * GICD_TYPER.ITLinesNumber gives (No. SPIS / 32) - 1, and we + * want to iterate over all blocks excluding 0 (which are the + * SGI/PPI interrupts, and not relevant here) + */ + for (block = (gicd.GICD_TYPER & ((1 << 5) - 1)); block > 0; --block) + SetSPISecurityBlock(block, group); +} + +/* EOF GICv3_gicd.c */ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicr.c b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicr.c new file mode 100644 index 00000000..26b5af8a --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/GICv3_gicr.c @@ -0,0 +1,308 @@ +/* + * GICv3_gicr.c - generic driver code for GICv3 redistributor + * + * Copyright (c) 2014-2019 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#include "GICv3.h" + +/* + * physical LPI Redistributor register map + */ +typedef struct +{ + volatile uint32_t GICR_CTLR; // +0x0000 - RW - Redistributor Control Register + const volatile uint32_t GICR_IIDR; // +0x0004 - RO - Implementer Identification Register + const volatile uint32_t GICR_TYPER[2]; // +0x0008 - RO - Redistributor Type Register + volatile uint32_t GICR_STATUSR; // +0x0010 - RW - Error Reporting Status Register, optional + volatile uint32_t GICR_WAKER; // +0x0014 - RW - Redistributor Wake Register + const volatile uint32_t padding1[2]; // +0x0018 - RESERVED +#ifndef USE_GIC600 + volatile uint32_t IMPDEF1[8]; // +0x0020 - ?? - IMPLEMENTATION DEFINED +#else + volatile uint32_t GICR_FCTLR; // +0x0020 - RW - Function Control Register + volatile uint32_t GICR_PWRR; // +0x0024 - RW - Power Management Control Register + volatile uint32_t GICR_CLASS; // +0x0028 - RW - Class Register + const volatile uint32_t padding2[5]; // +0x002C - RESERVED +#endif + volatile uint64_t GICR_SETLPIR; // +0x0040 - WO - Set LPI Pending Register + volatile uint64_t GICR_CLRLPIR; // +0x0048 - WO - Clear LPI Pending Register + const volatile uint32_t padding3[8]; // +0x0050 - RESERVED + volatile uint64_t GICR_PROPBASER; // +0x0070 - RW - Redistributor Properties Base Address Register + volatile uint64_t GICR_PENDBASER; // +0x0078 - RW - Redistributor LPI Pending Table Base Address Register + const volatile uint32_t padding4[8]; // +0x0080 - RESERVED + volatile uint64_t GICR_INVLPIR; // +0x00A0 - WO - Redistributor Invalidate LPI Register + const volatile uint32_t padding5[2]; // +0x00A8 - RESERVED + volatile uint64_t GICR_INVALLR; // +0x00B0 - WO - Redistributor Invalidate All Register + const volatile uint32_t padding6[2]; // +0x00B8 - RESERVED + volatile uint64_t GICR_SYNCR; // +0x00C0 - RO - Redistributor Synchronize Register + const volatile uint32_t padding7[2]; // +0x00C8 - RESERVED + const volatile uint32_t padding8[12]; // +0x00D0 - RESERVED + volatile uint64_t IMPDEF2; // +0x0100 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding9[2]; // +0x0108 - RESERVED + volatile uint64_t IMPDEF3; // +0x0110 - WO - IMPLEMENTATION DEFINED + const volatile uint32_t padding10[2]; // +0x0118 - RESERVED +} GICv3_redistributor_RD; + +/* + * SGI and PPI Redistributor register map + */ +typedef struct +{ + const volatile uint32_t padding1[32]; // +0x0000 - RESERVED + volatile uint32_t GICR_IGROUPR0; // +0x0080 - RW - Interrupt Group Registers (Security Registers in GICv1) + const volatile uint32_t padding2[31]; // +0x0084 - RESERVED + volatile uint32_t GICR_ISENABLER; // +0x0100 - RW - Interrupt Set-Enable Registers + const volatile uint32_t padding3[31]; // +0x0104 - RESERVED + volatile uint32_t GICR_ICENABLER; // +0x0180 - RW - Interrupt Clear-Enable Registers + const volatile uint32_t padding4[31]; // +0x0184 - RESERVED + volatile uint32_t GICR_ISPENDR; // +0x0200 - RW - Interrupt Set-Pending Registers + const volatile uint32_t padding5[31]; // +0x0204 - RESERVED + volatile uint32_t GICR_ICPENDR; // +0x0280 - RW - Interrupt Clear-Pending Registers + const volatile uint32_t padding6[31]; // +0x0284 - RESERVED + volatile uint32_t GICR_ISACTIVER; // +0x0300 - RW - Interrupt Set-Active Register + const volatile uint32_t padding7[31]; // +0x0304 - RESERVED + volatile uint32_t GICR_ICACTIVER; // +0x0380 - RW - Interrupt Clear-Active Register + const volatile uint32_t padding8[31]; // +0x0184 - RESERVED + volatile uint8_t GICR_IPRIORITYR[32]; // +0x0400 - RW - Interrupt Priority Registers + const volatile uint32_t padding9[504]; // +0x0420 - RESERVED + volatile uint32_t GICR_ICnoFGR[2]; // +0x0C00 - RW - Interrupt Configuration Registers + const volatile uint32_t padding10[62]; // +0x0C08 - RESERVED + volatile uint32_t GICR_IGRPMODR0; // +0x0D00 - RW - ???? + const volatile uint32_t padding11[63]; // +0x0D04 - RESERVED + volatile uint32_t GICR_NSACR; // +0x0E00 - RW - Non-Secure Access Control Register +} GICv3_redistributor_SGI; + +/* + * We have a multiplicity of GIC Redistributors; on the GIC-AEM and + * GIC-500 they are arranged as one 128KB region per redistributor: one + * 64KB page of GICR LPI registers, and one 64KB page of GICR Private + * Int registers + */ +typedef struct +{ + union + { + GICv3_redistributor_RD RD_base; + uint8_t padding[64 * 1024]; + } RDblock; + + union + { + GICv3_redistributor_SGI SGI_base; + uint8_t padding[64 * 1024]; + } SGIblock; +} GICv3_GICR; + +/* + * use the scatter file to place GIC Redistributor base address + * + * although this code doesn't know how many Redistributor banks + * a particular system will have, we declare gicrbase as an array + * to avoid unwanted compiler optimisations when calculating the + * base of a particular Redistributor bank + */ +static const GICv3_GICR gicrbase[2] __attribute__((section (".gicr"))); + +/**********************************************************************/ + +/* + * utility functions to calculate base of a particular + * Redistributor bank + */ + +static inline GICv3_redistributor_RD *const getgicrRD(uint32_t gicr) +{ + GICv3_GICR *const arraybase = (GICv3_GICR *const)&gicrbase; + + return &((arraybase + gicr)->RDblock.RD_base); +} + +static inline GICv3_redistributor_SGI *const getgicrSGI(uint32_t gicr) +{ + GICv3_GICR *arraybase = (GICv3_GICR *)(&gicrbase); + + return &(arraybase[gicr].SGIblock.SGI_base); +} + +/**********************************************************************/ + +// This function walks a block of RDs to find one with the matching affinity +uint32_t GetGICR(uint32_t affinity) +{ + GICv3_redistributor_RD* gicr; + uint32_t index = 0; + + do + { + gicr = getgicrRD(index); + if (gicr->GICR_TYPER[1] == affinity) + return index; + + index++; + } + while((gicr->GICR_TYPER[0] & (1<<4)) == 0); // Keep looking until GICR_TYPER.Last reports no more RDs in block + + return 0xFFFFFFFF; // return -1 to signal not RD found +} + +void WakeupGICR(uint32_t gicr) +{ + GICv3_redistributor_RD *const gicrRD = getgicrRD(gicr); +#ifdef USE_GIC600 + //Power up Re-distributor for GIC-600 + gicrRD->GICR_PWRR = 0x2; +#endif + + /* + * step 1 - ensure GICR_WAKER.ProcessorSleep is off + */ + gicrRD->GICR_WAKER &= ~gicrwaker_ProcessorSleep; + + /* + * step 2 - wait for children asleep to be cleared + */ + while ((gicrRD->GICR_WAKER & gicrwaker_ChildrenAsleep) != 0) + continue; + + /* + * OK, GICR is go + */ + return; +} + +void EnablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ISENABLER = 1 << id; +} + +void DisablePrivateInt(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + id &= 0x1f; + + gicrSGI->GICR_ICENABLER = 1 << id; +} + +void SetPrivateIntPriority(uint32_t gicr, uint32_t id, uint32_t priority) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + gicrSGI->GICR_IPRIORITYR[id] = priority; +} + +uint32_t GetPrivateIntPriority(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICD_IPRIORITYR has one byte-wide entry per interrupt + */ + id &= RANGE_LIMIT(gicrSGI->GICR_IPRIORITYR); + + return (uint32_t)(gicrSGI->GICR_IPRIORITYR[id]); +} + +void SetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ISPENDR = 1 << id; +} + +void ClearPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ICPENDR is one 32-bit register + */ + id &= 0x1f; + + gicrSGI->GICR_ICPENDR = 1 << id; +} + +uint32_t GetPrivateIntPending(uint32_t gicr, uint32_t id) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + + /* + * GICR_ISPENDR is one 32-bit register + */ + id &= 0x1f; + + return (gicrSGI->GICR_ISPENDR >> id) & 0x01; +} + +void SetPrivateIntSecurity(uint32_t gicr, uint32_t id, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + uint32_t groupmod; + + /* + * GICR_IGROUPR0 is one 32-bit register + */ + id &= 0x1f; + + /* + * the single group argument is split into two separate + * registers, so filter out and remove the (new to gicv3) + * group modifier bit + */ + groupmod = (group >> 1) & 1; + group &= 1; + + /* + * either set or clear the Group bit for the interrupt as appropriate + */ + if (group) + gicrSGI->GICR_IGROUPR0 |= 1 << id; + else + gicrSGI->GICR_IGROUPR0 &= ~(1 << id); + + /* + * now deal with groupmod + */ + if (groupmod) + gicrSGI->GICR_IGRPMODR0 |= 1 << id; + else + gicrSGI->GICR_IGRPMODR0 &= ~(1 << id); +} + +void SetPrivateIntSecurityBlock(uint32_t gicr, GICIGROUPRBits_t group) +{ + GICv3_redistributor_SGI *const gicrSGI = getgicrSGI(gicr); + const uint32_t nbits = (sizeof group * 8) - 1; + uint32_t groupmod; + + /* + * get each bit of group config duplicated over all 32-bits + */ + groupmod = (uint32_t)(((int32_t)group << (nbits - 1)) >> 31); + group = (uint32_t)(((int32_t)group << nbits) >> 31); + + /* + * set the security state for this block of SPIs + */ + gicrSGI->GICR_IGROUPR0 = group; + gicrSGI->GICR_IGRPMODR0 = groupmod; +} + +/* EOF GICv3_gicr.c */ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.S b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.S new file mode 100644 index 00000000..c787c3f5 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.S @@ -0,0 +1,133 @@ +// +// Armv8-A AArch64 - Basic Mutex Example +// Includes the option (USE_LSE_ATOMIC) to use Large System Extension (LSE) atomics introduced in Armv8.1-A +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + + .global _mutex_initialize + .global _mutex_acquire + .global _mutex_release + +// +// These routines implement the mutex management functions required for running +// the Arm C library in a multi-threaded environment. +// +// They use a value of 0 to represent an unlocked mutex, and 1 for a locked mutex +// +// ********************************************************************** +// + + .type _mutex_initialize, "function" + .cfi_startproc +_mutex_initialize: + + // + // mark the mutex as unlocked + // + mov w1, #0 + str w1, [x0] + + // + // we are running multi-threaded, so set a non-zero return + // value (function prototype says use 1) + // + mov w0, #1 + ret + .cfi_endproc + +#if !defined(USE_LSE_ATOMIC) + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + + // + // send ourselves an event, so we don't stick on the wfe at the + // top of the loop + // + sevl + + // + // wait until the mutex is available + // +loop: + wfe + ldaxr w1, [x0] + cbnz w1, loop + + // + // mutex is (at least, it was) available - try to claim it + // + mov w1, #1 + stxr w2, w1, [x0] + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + + mov w1, #0 + stlr w1, [x0] + ret + .cfi_endproc + +#else // LSE version + + .type _mutex_acquire, "function" + .cfi_startproc +_mutex_acquire: + // This uses a "ticket lock". The lock is stored as a 32-bit value: + // - the upper 16-bits record the thread's ticket number ("take a ticket") + // - the lower 16-bits record the ticket being served ("now serving") + + // atomically load then increment the thread's ticket number ("take a ticket") + mov w3, #(1 << 16) + ldadda w3, w1, [x0] + + // is the ticket now being served? + eor w2, w1, w1, ror #16 + cbz w2, loop_exit + + // no, so wait for the ticket to be served + + // send a local event to avoid missing an unlock before the exclusive load + sevl + +loop: + wfe + ldaxrh w3, [x0] + eor w2, w3, w1, lsr #16 + cbnz w2, loop + + // + // OK, we have the mutex, our work is done here + // +loop_exit: + ret + .cfi_endproc + + + .type _mutex_release, "function" + .cfi_startproc +_mutex_release: + mov w1, #1 + staddlh w1, [x0] + ret + .cfi_endproc +#endif diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.h new file mode 100644 index 00000000..ec1a1d28 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/MP_Mutexes.h @@ -0,0 +1,66 @@ +/* + * Armv8-A AArch64 - Basic Mutex Example + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ +#ifndef MP_MUTEX_H +#define MP_MUTEX_H + +/* + * The Arm C library calls-out to these functions to manage multithreading. + * They can also be called by user application code. + * + * Mutex type is specified by the Arm C library + * + * Declare function prototypes for libc mutex routines + */ +typedef signed int *mutex; + +/* + * int _mutex_initialize(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * 0 - application is non-threaded + * 1 - application is threaded + * The C library uses the return result to indicate whether it is being used in a multithreaded environment. + */ +int _mutex_initialize(mutex *m); + +/* + * void _mutex_acquire(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * Routine does not return until the mutex has been claimed. A load-acquire + * is used to guarantee that the mutex claim is properly ordered with + * respect to any accesses to the resource protected by the mutex + */ +void _mutex_acquire(mutex *m); + +/* + * void _mutex_release(mutex *m) + * + * Inputs + * mutex *m - pointer to the 32-bit word associated with the mutex + * + * Returns + * + * + * Side Effects + * A store-release is used to guarantee that the mutex release is properly + * ordered with respect any accesses to the resource protected by the mutex + */ +void _mutex_release(mutex *m); + +#endif diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/PPM_AEM.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/PPM_AEM.h new file mode 100644 index 00000000..f7501eeb --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/PPM_AEM.h @@ -0,0 +1,66 @@ +// +// Private Peripheral Map for the v8 Architecture Envelope Model +// +// Copyright (c) 2012-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef PPM_AEM_H +#define PPM_AEM_H + +// +// Distributor layout +// +#define GICD_CTLR 0x0000 +#define GICD_TYPER 0x0004 +#define GICD_IIDR 0x0008 +#define GICD_IGROUP 0x0080 +#define GICD_ISENABLE 0x0100 +#define GICD_ICENABLE 0x0180 +#define GICD_ISPEND 0x0200 +#define GICD_ICPEND 0x0280 +#define GICD_ISACTIVE 0x0300 +#define GICD_ICACTIVE 0x0380 +#define GICD_IPRIORITY 0x0400 +#define GICD_ITARGETS 0x0800 +#define GICD_ICFG 0x0c00 +#define GICD_PPISR 0x0d00 +#define GICD_SPISR 0x0d04 +#define GICD_SGIR 0x0f00 +#define GICD_CPENDSGI 0x0f10 +#define GICD_SPENDSGI 0x0f20 +#define GICD_PIDR4 0x0fd0 +#define GICD_PIDR5 0x0fd4 +#define GICD_PIDR6 0x0fd8 +#define GICD_PIDR7 0x0fdc +#define GICD_PIDR0 0x0fe0 +#define GICD_PIDR1 0x0fe4 +#define GICD_PIDR2 0x0fe8 +#define GICD_PIDR3 0x0fec +#define GICD_CIDR0 0x0ff0 +#define GICD_CIDR1 0x0ff4 +#define GICD_CIDR2 0x0ff8 +#define GICD_CIDR3 0x0ffc + +// +// CPU Interface layout +// +#define GICC_CTLR 0x0000 +#define GICC_PMR 0x0004 +#define GICC_BPR 0x0008 +#define GICC_IAR 0x000c +#define GICC_EOIR 0x0010 +#define GICC_RPR 0x0014 +#define GICC_HPPIR 0x0018 +#define GICC_ABPR 0x001c +#define GICC_AIAR 0x0020 +#define GICC_AEOIR 0x0024 +#define GICC_AHPPIR 0x0028 +#define GICC_APR0 0x00d0 +#define GICC_NSAPR0 0x00e0 +#define GICC_IIDR 0x00fc +#define GICC_DIR 0x1000 + +#endif // PPM_AEM_H diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.c b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.c new file mode 100644 index 00000000..17cceb01 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.c @@ -0,0 +1,393 @@ +/* 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" + + +extern void init_timer(void); /* in timer_interrupts.c */ + + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define byte pool memory. */ + +UCHAR byte_pool_memory[DEMO_BYTE_POOL_SIZE]; + + + + +/* 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_TIMER timer_0; +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; + + +/* 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); + + +#ifdef TX_ENABLE_EVENT_TRACE + +UCHAR event_buffer[65536]; + +#endif + + +/* Define main entry point. */ + +int main(void) +{ + + /* Initialize timer. */ + init_timer(); + + /* Enter ThreadX. */ + tx_kernel_enter(); + + return 0; +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + +#ifdef TX_ENABLE_EVENT_TRACE + + tx_trace_enable(event_buffer, sizeof(event_buffer), 32); +#endif + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", byte_pool_memory, DEMO_BYTE_POOL_SIZE); + + /* 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_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.launch b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.launch new file mode 100644 index 00000000..d583631e --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.launch @@ -0,0 +1,445 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.ld b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.ld new file mode 100644 index 00000000..e9b12a82 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sample_threadx.ld @@ -0,0 +1,245 @@ +/* Linker script to place sections and symbol values. + * It references following symbols, which must be defined in code: + * start64 : Entry point + * + * It defines following symbols, which code can use without definition: + * __cs3_peripherals + * __code_start + * __exidx_start + * __exidx_end + * __data_start + * __preinit_array_start + * __preinit_array_end + * __init_array_start + * __init_array_end + * __fini_array_start + * __fini_array_end + * __bss_start__ + * __bss_end__ + * __end__ + * __stack + * __el3_stack + * __ttb0_l1 + * __ttb0_l2_ram + * __ttb0_l2_private + * __ttb0_l2_periph + * __top_of_ram + */ + +ENTRY(start64) + +SECTIONS +{ + /* + * CS3 Peripherals is a 64MB region from 0x1c000000 + * that includes the following: + * System Registers at 0x1C010000 + * UART0 (PL011) at 0x1C090000 + * Color LCD Controller (PL111) at 0x1C1F0000 + * plus a number of others. + * CS3_PERIPHERALS is used by the startup code for page-table generation + * This region is not truly empty, but we have no + * predefined objects that live within it + */ + __cs3_peripherals = 0x1c000000; + + /* + * GICv3 distributor + */ + .gicd 0x2f000000 (NOLOAD): + { + *(.gicd) + } + + /* + * GICv3 redistributors + * 128KB for each redistributor in the system + */ + .gicr 0x2f100000 (NOLOAD): + { + *(.gicr) + } + + .vectors 0x80000000: + { + __code_start = .; + KEEP(*(StartUp)) + KEEP(*(EL1VECTORS EL2VECTORS EL3VECTORS)) + } + + .init : + { + KEEP (*(SORT_NONE(.init))) + } + + .text : + { + *(.text*) + } + + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + } + + .eh_frame : + { + KEEP (*(.eh_frame)) + } + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } + + .ARM.exidx : + { + __exidx_start = .; + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + __exidx_end = .; + } + + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array )) + PROVIDE_HIDDEN (__init_array_end = .); + } + + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array )) + PROVIDE_HIDDEN (__fini_array_end = .); + } + + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + + .jcr : + { + KEEP (*(.jcr)) + } + + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } + + .heap (NOLOAD): + { + . = ALIGN(64); + __end__ = .; + PROVIDE(end = .); + . = . + 0x1000; + } + + .stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x8000; + __stack = .; + } + + .handler_stack_limit (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x4000; + handler_stack_limit = .; + } + + .el3_stack (NOLOAD): + { + . = ALIGN(64); + . = . + 4 * 0x1000; + __el3_stack = .; + } + + .ttb0_l1 (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l1 = .; + . = . + 0x1000; + } + + .ttb0_l2_ram (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_ram = .; + . = . + 0x1000; + } + + .ttb0_l2_private (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_private = .; + . = . + 0x1000; + } + + .ttb0_l2_periph (NOLOAD): + { + . = ALIGN(4096); + __ttb0_l2_periph = .; + . = . + 0x1000; + } + + /* + * The startup code uses the end of this region to calculate + * the top of memory - don't place any RAM regions after it + */ + __top_of_ram = .; +} diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.c b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.c new file mode 100644 index 00000000..c2ce6faa --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.c @@ -0,0 +1,122 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "sp804_timer.h" + +#define TIMER_SP804_CTRL_TIMEREN (1 << 7) +#define TIMER_SP804_CTRL_TIMERMODE (1 << 6) // Bit 6: +#define TIMER_SP804_CTRL_INTENABLE (1 << 5) +#define TIMER_SP804_CTRL_TIMERSIZE (1 << 1) // Bit 1: 0=16-bit, 1=32-bit +#define TIMER_SP804_CTRL_ONESHOT (1 << 0) // Bit 0: 0=wrapping, 1=one-shot + +#define TIMER_SP804_CTRL_PRESCALE_1 (0 << 2) // clk/1 +#define TIMER_SP804_CTRL_PRESCALE_4 (1 << 2) // clk/4 +#define TIMER_SP804_CTRL_PRESCALE_8 (2 << 2) // clk/8 + +struct sp804_timer +{ + volatile uint32_t Time1Load; // +0x00 + const volatile uint32_t Time1Value; // +0x04 - RO + volatile uint32_t Timer1Control; // +0x08 + volatile uint32_t Timer1IntClr; // +0x0C - WO + const volatile uint32_t Timer1RIS; // +0x10 - RO + const volatile uint32_t Timer1MIS; // +0x14 - RO + volatile uint32_t Timer1BGLoad; // +0x18 + + volatile uint32_t Time2Load; // +0x20 + volatile uint32_t Time2Value; // +0x24 + volatile uint8_t Timer2Control; // +0x28 + volatile uint32_t Timer2IntClr; // +0x2C - WO + const volatile uint32_t Timer2RIS; // +0x30 - RO + const volatile uint32_t Timer2MIS; // +0x34 - RO + volatile uint32_t Timer2BGLoad; // +0x38 + + // Not including ID registers + +}; + +// Instance of the dual timer, will be placed using the scatter file +struct sp804_timer* dual_timer; + + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address) +{ + dual_timer = (struct sp804_timer*)address; + return; +} + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt) +{ + uint32_t tmp = 0; + + dual_timer->Time1Load = load_value; + + // Fixed setting: 32-bit, no prescaling + tmp = TIMER_SP804_CTRL_TIMERSIZE | TIMER_SP804_CTRL_PRESCALE_1 | TIMER_SP804_CTRL_TIMERMODE; + + // Settings from parameters: interrupt generation & reload + tmp = tmp | interrupt | auto_reload; + + // Write control register + dual_timer->Timer1Control = tmp; + + return; +} + + +// Starts the timer +void startTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp | TIMER_SP804_CTRL_TIMEREN; // Set TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Stops the timer +void stopTimer(void) +{ + uint32_t tmp; + + tmp = dual_timer->Timer1Control; + tmp = tmp & ~TIMER_SP804_CTRL_TIMEREN; // Clear TimerEn (bit 7) + dual_timer->Timer1Control = tmp; + + return; +} + + +// Returns the current timer count +uint32_t getTimerCount(void) +{ + return dual_timer->Time1Value; +} + + +void clearTimerIrq(void) +{ + // A write to this register, of any value, clears the interrupt + dual_timer->Timer1IntClr = 1; +} + + +// ------------------------------------------------------------ +// End of sp804_timer.c +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.h new file mode 100644 index 00000000..4d423904 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/sp804_timer.h @@ -0,0 +1,53 @@ +// ------------------------------------------------------------ +// SP804 Dual Timer +// Header Filer +// +// Copyright (c) 2009-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#ifndef _SP804_TIMER_ +#define _SP804_TIMER_ + +#include + +// Set base address of timer +// address - virtual address of SP804 timer +void setTimerBaseAddress(uint64_t address); + + +// Sets up the private timer +// load_value - Initial value of timer +// auto_reload - Periodic (SP804_AUTORELOAD) or one shot (SP804_SINGLESHOT) +// interrupt - Whether to generate an interrupt + +#define SP804_AUTORELOAD (0) +#define SP804_SINGLESHOT (1) +#define SP804_GENERATE_IRQ (1 << 5) +#define SP804_NO_IRQ (0) + +void initTimer(uint32_t load_value, uint32_t auto_reload, uint32_t interrupt); + + +// Starts the timer +void startTimer(void); + + +// Stops the timer +void stopTimer(void); + + +// Returns the current timer count +uint32_t getTimerCount(void); + + +// Clears the timer interrupt +void clearTimerIrq(void); + +#endif + +// ------------------------------------------------------------ +// End of sp804_timer.h +// ------------------------------------------------------------ diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/startup.S b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/startup.S new file mode 100644 index 00000000..2d59722d --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/startup.S @@ -0,0 +1,798 @@ +// ------------------------------------------------------------ +// Armv8-A MPCore EL3 AArch64 Startup Code +// +// Basic Vectors, MMU, caches and GICv3 initialization +// +// Exits in EL1 AArch64 +// +// Copyright (c) 2014-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_mmu.h" +#include "v8_system.h" +#include "GICv3_aliases.h" + + .section StartUp, "ax" + .balign 4 + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + + .global InvalidateUDCaches + .global ZeroBlock + + .global SetPrivateIntSecurityBlock + .global SetSPISecurityAll + .global SetPrivateIntPriority + + .global WakeupGICR + .global SyncAREinGICD + .global EnableGICD + .global EnablePrivateInt + .global GetPrivateIntPending + .global ClearPrivateIntPending + + .global _start + .global MainApp + + .global __code_start + .global __ttb0_l1 + .global __ttb0_l2_ram + .global __ttb0_l2_periph + .global __top_of_ram + .global gicd + .global __stack + .global __el3_stack + .global __cs3_peripherals + +is_mmu_ready: +.word 0 + + +// ------------------------------------------------------------ + + .global start64 + .type start64, "function" +start64: + + // + // program the VBARs + // + ldr x1, =el1_vectors + msr VBAR_EL1, x1 + + ldr x1, =el2_vectors + msr VBAR_EL2, x1 + + ldr x1, =el3_vectors + msr VBAR_EL3, x1 + + + // GIC-500 comes out of reset in GICv2 compatibility mode - first set + // system register enables for all relevant exception levels, and + // select GICv3 operating mode + // + msr SCR_EL3, xzr // Ensure NS bit is initially clear, so secure copy of ICC_SRE_EL1 can be configured + isb + + mov x0, #15 + msr ICC_SRE_EL3, x0 + isb + msr ICC_SRE_EL1, x0 // Secure copy of ICC_SRE_EL1 + + // + // set lower exception levels as non-secure, with no access + // back to EL2 or EL3, and are AArch64 capable + // + mov x3, #(SCR_EL3_RW | \ + SCR_EL3_SMD | \ + SCR_EL3_NS) // Set NS bit, to access Non-secure registers + msr SCR_EL3, x3 + isb + + mov x0, #15 + msr ICC_SRE_EL2, x0 + isb + msr ICC_SRE_EL1, x0 // Non-secure copy of ICC_SRE_EL1 + + + // + // no traps or VM modifications from the Hypervisor, EL1 is AArch64 + // + mov x2, #HCR_EL2_RW + msr HCR_EL2, x2 + + // + // VMID is still significant, even when virtualisation is not + // being used, so ensure VTTBR_EL2 is properly initialised + // + msr VTTBR_EL2, xzr + + // + // VMPIDR_EL2 holds the value of the Virtualization Multiprocessor ID. This is the value returned by Non-secure EL1 reads of MPIDR_EL1. + // VPIDR_EL2 holds the value of the Virtualization Processor ID. This is the value returned by Non-secure EL1 reads of MIDR_EL1. + // Both of these registers are architecturally UNKNOWN at reset, and so they must be set to the correct value + // (even if EL2/virtualization is not being used), otherwise non-secure EL1 reads of MPIDR_EL1/MIDR_EL1 will return garbage values. + // This guarantees that any future reads of MPIDR_EL1 and MIDR_EL1 from Non-secure EL1 will return the correct value. + // + mrs x0, MPIDR_EL1 + msr VMPIDR_EL2, x0 + mrs x0, MIDR_EL1 + msr VPIDR_EL2, x0 + + // extract the core number from MPIDR_EL1 and store it in + // x19 (defined by the AAPCS as callee-saved), so we can re-use + // the number later + // + bl GetCPUID + mov x19, x0 + + // + // neither EL3 nor EL2 trap floating point or accesses to CPACR + // + msr CPTR_EL3, xzr + msr CPTR_EL2, xzr + + // + // SCTLR_ELx may come out of reset with UNKNOWN values so we will + // set the fields to 0 except, possibly, the endianess field(s). + // Note that setting SCTLR_EL2 or the EL0 related fields of SCTLR_EL1 + // is not strictly needed, since we're never in EL2 or EL0 + // +#ifdef __ARM_BIG_ENDIAN + mov x0, #(SCTLR_ELx_EE | SCTLR_EL1_E0E) +#else + mov x0, #0 +#endif + msr SCTLR_EL3, x0 + msr SCTLR_EL2, x0 + msr SCTLR_EL1, x0 + +#ifdef CORTEXA + // + // Configure ACTLR_EL[23] + // ---------------------- + // + // These bits are IMPLEMENTATION DEFINED, so are different for + // different processors + // + // For Cortex-A57, the controls we set are: + // + // Enable lower level access to CPUACTLR_EL1 + // Enable lower level access to CPUECTLR_EL1 + // Enable lower level access to L2CTLR_EL1 + // Enable lower level access to L2ECTLR_EL1 + // Enable lower level access to L2ACTLR_EL1 + // + mov x0, #((1 << 0) | \ + (1 << 1) | \ + (1 << 4) | \ + (1 << 5) | \ + (1 << 6)) + + msr ACTLR_EL3, x0 + msr ACTLR_EL2, x0 + + // + // configure CPUECTLR_EL1 + // + // These bits are IMP DEF, so need to different for different + // processors + // + // SMPEN - bit 6 - Enables the processor to receive cache + // and TLB maintenance operations + // + // Note: For Cortex-A57/53 SMPEN should be set before enabling + // the caches and MMU, or performing any cache and TLB + // maintenance operations. + // + // This register has a defined reset value, so we use a + // read-modify-write sequence to set SMPEN + // + mrs x0, S3_1_c15_c2_1 // Read EL1 CPU Extended Control Register + orr x0, x0, #(1 << 6) // Set the SMPEN bit + msr S3_1_c15_c2_1, x0 // Write EL1 CPU Extended Control Register + + isb +#endif + + // + // That's the last of the control settings for now + // + // Note: no ISB after all these changes, as registers won't be + // accessed until after an exception return, which is itself a + // context synchronisation event + // + + // + // Setup some EL3 stack space, ready for calling some subroutines, below. + // + // Stack space allocation is CPU-specific, so use CPU + // number already held in x19 + // + // 2^12 bytes per CPU for the EL3 stacks + // + ldr x0, =__el3_stack + sub x0, x0, x19, lsl #12 + mov sp, x0 + + // + // we need to configure the GIC while still in secure mode, specifically + // all PPIs and SPIs have to be programmed as Group1 interrupts + // + + // + // Before the GIC can be reliably programmed, we need to + // enable Affinity Routing, as this affects where the configuration + // registers are (with Affinity Routing enabled, some registers are + // in the Redistributor, whereas those same registers are in the + // Distributor with Affinity Routing disabled (i.e. when in GICv2 + // compatibility mode). + // + mov x0, #(1 << 4) | (1 << 5) // gicdctlr_ARE_S | gicdctlr_ARE_NS + mov x1, x19 + bl SyncAREinGICD + + // + // The Redistributor comes out of reset assuming the processor is + // asleep - correct that assumption + // + mov w0, w19 + bl WakeupGICR + + // + // Now we're ready to set security and other initialisations + // + // This is a per-CPU configuration for these interrupts + // + // for the first cluster, CPU number is the redistributor index + // + mov w0, w19 + mov w1, #1 // gicigroupr_G1NS + bl SetPrivateIntSecurityBlock + + // + // While we're in the Secure World, set the priority mask low enough + // for it to be writable in the Non-Secure World + // + //mov x0, #16 << 3 // 5 bits of priority in the Secure world + mov x0, #0xFF // for Non-Secure interrupts + msr ICC_PMR_EL1, x0 + + // + // there's more GIC setup to do, but only for the primary CPU + // + cbnz x19, drop_to_el1 + + // + // There's more to do to the GIC - call the utility routine to set + // all SPIs to Group1 + // + mov w0, #1 // gicigroupr_G1NS + bl SetSPISecurityAll + + // + // Set up EL1 entry point and "dummy" exception return information, + // then perform exception return to enter EL1 + // + .global drop_to_el1 +drop_to_el1: + adr x1, el1_entry_aarch64 + msr ELR_EL3, x1 + mov x1, #(AARCH64_SPSR_EL1h | \ + AARCH64_SPSR_F | \ + AARCH64_SPSR_I | \ + AARCH64_SPSR_A) + msr SPSR_EL3, x1 + eret + + + +// ------------------------------------------------------------ +// EL1 - Common start-up code +// ------------------------------------------------------------ + + .global el1_entry_aarch64 + .type el1_entry_aarch64, "function" +el1_entry_aarch64: + + // + // Now we're in EL1, setup the application stack + // the scatter file allocates 2^14 bytes per app stack + // + ldr x0, =handler_stack_limit + sub x0, x0, x19, lsl #14 + mov sp, x0 + MSR SPSel, #0 + ISB + ldr x0, =__stack + sub x0, x0, x19, lsl #14 + mov sp, x0 + + // + // Enable floating point + // + mov x0, #CPACR_EL1_FPEN + msr CPACR_EL1, x0 + + // + // Invalidate caches and TLBs for all stage 1 + // translations used at EL1 + // + // Cortex-A processors automatically invalidate their caches on reset + // (unless suppressed with the DBGL1RSTDISABLE or L2RSTDISABLE pins). + // It is therefore not necessary for software to invalidate the caches + // on startup, however, this is done here in case of a warm reset. + bl InvalidateUDCaches + tlbi VMALLE1 + + + // + // Set TTBR0 Base address + // + // The CPUs share one set of translation tables that are + // generated by CPU0 at run-time + // + // TTBR1_EL1 is not used in this example + // + ldr x1, =__ttb0_l1 + msr TTBR0_EL1, x1 + + + // + // Set up memory attributes + // + // These equate to: + // + // 0 -> 0b01000100 = 0x00000044 = Normal, Inner/Outer Non-Cacheable + // 1 -> 0b11111111 = 0x0000ff00 = Normal, Inner/Outer WriteBack Read/Write Allocate + // 2 -> 0b00000100 = 0x00040000 = Device-nGnRE + // + mov x1, #0xff44 + movk x1, #4, LSL #16 // equiv to: movk x1, #0x0000000000040000 + msr MAIR_EL1, x1 + + + // + // Set up TCR_EL1 + // + // We're using only TTBR0 (EPD1 = 1), and the page table entries: + // - are using an 8-bit ASID from TTBR0 + // - have a 4K granularity (TG0 = 0b00) + // - are outer-shareable (SH0 = 0b10) + // - are using Inner & Outer WBWA Normal memory ([IO]RGN0 = 0b01) + // - map + // + 32 bits of VA space (T0SZ = 0x20) + // + into a 32-bit PA space (IPS = 0b000) + // + // 36 32 28 24 20 16 12 8 4 0 + // -----+----+----+----+----+----+----+----+----+----+ + // | | |OOII| | | |OOII| | | + // TT | | |RRRR|E T | T| |RRRR|E T | T| + // BB | I I|TTSS|GGGG|P 1 | 1|TTSS|GGGG|P 0 | 0| + // IIA| P P|GGHH|NNNN|DAS | S|GGHH|NNNN|D S | S| + // 10S| S-S|1111|1111|11Z-|---Z|0000|0000|0 Z-|---Z| + // + // 000 0000 0000 0000 1000 0000 0010 0101 0010 0000 + // + // 0x 8 0 2 5 2 0 + // + // Note: the ISB is needed to ensure the changes to system + // context are before the write of SCTLR_EL1.M to enable + // the MMU. It is likely on a "real" implementation that + // this setup would work without an ISB, due to the + // amount of code that gets executed before enabling the + // MMU, but that would not be architecturally correct. + // + ldr x1, =0x0000000000802520 + msr TCR_EL1, x1 + isb + + // + // the primary CPU is going to use SGI 15 as a wakeup event + // to let us know when it is OK to proceed, so prepare for + // receiving that interrupt + // + // NS interrupt priorities run from 0 to 15, with 15 being + // too low a priority to ever raise an interrupt, so let's + // use 14 + // + mov w0, w19 + mov w1, #0 + mov w2, #14 << 4 // we're in NS world, so 4 bits of priority, + // 8-bit field, - 4 = 4-bit shift + bl SetPrivateIntPriority + + mov w0, w19 + mov w1, #0 + bl EnablePrivateInt + + // + // set priority mask as low as possible; although,being in the + // NS World, we can't set bit[7] of the priority, we still + // write all 8-bits of priority to an ICC register + // + mov x0, #31 << 3 + msr ICC_PMR_EL1, x0 + + // + // set global enable and wait for our interrupt to arrive + // + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + isb + + // + // x19 already contains the CPU number, so branch to secondary + // code if we're not on CPU0 + // + cbnz x19, el1_secondary + + // + // Fall through to primary code + // + + +// +// ------------------------------------------------------------ +// +// EL1 - primary CPU init code +// +// This code is run on CPU0, while the other CPUs are in the +// holding pen +// + + .global el1_primary + .type el1_primary, "function" +el1_primary: + + // + // Turn on the banked GIC distributor enable, + // ready for individual CPU enables later + // + mov w0, #(1 << 1) // gicdctlr_EnableGrp1A + bl EnableGICD + + // + // Generate TTBR0 L1 + // + // at 4KB granularity, 32-bit VA space, table lookup starts at + // L1, with 1GB regions + // + // we are going to create entries pointing to L2 tables for a + // couple of these 1GB regions, the first of which is the + // RAM on the VE board model - get the table addresses and + // start by emptying out the L1 page tables (4 entries at L1 + // for a 4K granularity) + // + // x21 = address of L1 tables + // + ldr x21, =__ttb0_l1 + mov x0, x21 + mov x1, #(4 << 3) + bl ZeroBlock + + // + // time to start mapping the RAM regions - clear out the + // L2 tables and point to them from the L1 tables + // + // x22 = address of L2 tables, needs to be remembered in case + // we want to re-use the tables for mapping peripherals + // + ldr x22, =__ttb0_l2_ram + mov x1, #(512 << 3) + mov x0, x22 + bl ZeroBlock + + // + // Get the start address of RAM (the EXEC region) into x4 + // and calculate the offset into the L1 table (1GB per region, + // max 4GB) + // + // x23 = L1 table offset, saved for later comparison against + // peripheral offset + // + ldr x4, =__code_start + ubfx x23, x4, #30, #2 + + orr x1, x22, #TT_S1_ATTR_PAGE + str x1, [x21, x23, lsl #3] + + // + // we've already used the RAM start address in x4 - we now need + // to get this in terms of an offset into the L2 page tables, + // where each entry covers 2MB + // + ubfx x2, x4, #21, #9 + + // + // TOP_OF_RAM in the scatter file marks the end of the + // Execute region in RAM: convert the end of this region to an + // offset too, being careful to round up, then calculate the + // number of entries to write + // + ldr x5, =__top_of_ram + sub x3, x5, #1 + ubfx x3, x3, #21, #9 + add x3, x3, #1 + sub x3, x3, x2 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as Shared, Normal WBWA (MAIR[1]) with a flat + // VA->PA translation + // + bic x4, x4, #((1 << 21) - 1) + mov x1, #(TT_S1_ATTR_BLOCK | \ + (1 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_SH_INNER | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // factor the offset into the page table address and then write + // the entries + // + add x0, x22, x2, lsl #3 + +loop1: + subs x3, x3, #1 + str x1, [x0], #8 + add x1, x1, #0x200, LSL #12 // equiv to add x1, x1, #(1 << 21) // 2MB per entry + bne loop1 + + + // + // now mapping the Peripheral regions - clear out the + // L2 tables and point to them from the L1 tables + // + // The assumption here is that all peripherals live within + // a common 1GB region (i.e. that there's a single set of + // L2 pages for all the peripherals). We only use a UART + // and the GIC in this example, so the assumption is sound + // + // x24 = address of L2 peripheral tables + // + ldr x24, =__ttb0_l2_periph + + // + // get the GICD address into x4 and calculate + // the offset into the L1 table + // + // x25 = L1 table offset + // + ldr x4, =gicd + ubfx x25, x4, #30, #2 + + // + // here's the tricky bit: it's possible that the peripherals are + // in the same 1GB region as the RAM, in which case we don't need + // to prime a separate set of L2 page tables, nor add them to the + // L1 tables + // + // if we're going to re-use the TTB0_L2_RAM tables, get their + // address into x24, which is used later on to write the PTEs + // + cmp x25, x23 + csel x24, x22, x24, EQ + b.eq nol2setup + + // + // Peripherals are in a separate 1GB region, and so have their own + // set of L2 tables - clean out the tables and add them to the L1 + // table + // + mov x0, x24 + mov x1, #512 << 3 + bl ZeroBlock + + orr x1, x24, #TT_S1_ATTR_PAGE + str x1, [x21, x25, lsl #3] + + // + // there's only going to be a single 2MB region for GICD (in + // x4) - get this in terms of an offset into the L2 page tables + // + // with larger systems, it is possible that the GIC redistributor + // registers require extra 2MB pages, in which case extra code + // would be required here + // +nol2setup: + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry for this, so no loop as we have for RAM, above + // + str x1, [x24, x2, lsl #3] + + // + // we have CS3_PERIPHERALS that include the UART controller + // + // Again, the code is making assumptions - this time that the CS3_PERIPHERALS + // region uses the same 1GB portion of the address space as the GICD, + // and thus shares the same set of L2 page tables + // + // Get CS3_PERIPHERALS address into x4 and calculate the offset into the + // L2 tables + // + ldr x4, =__cs3_peripherals + ubfx x2, x4, #21, #9 + + // + // set x1 to the required page table attributes, then orr + // in the start address (modulo 2MB) + // + // L2 tables in our configuration cover 2MB per entry - map + // memory as NS Device-nGnRE (MAIR[2]) with a flat VA->PA + // translation + // + bic x4, x4, #((1 << 21) - 1) // start address mod 2MB + mov x1, #(TT_S1_ATTR_BLOCK | \ + (2 << TT_S1_ATTR_MATTR_LSB) | \ + TT_S1_ATTR_NS | \ + TT_S1_ATTR_AP_RW_PL1 | \ + TT_S1_ATTR_AF | \ + TT_S1_ATTR_nG) + orr x1, x1, x4 + + // + // only a single L2 entry again - write it + // + str x1, [x24, x2, lsl #3] + + // + // issue a barrier to ensure all table entry writes are complete + // + dsb ish + + ldr x1, =is_mmu_ready + mov x2, #1 + str x2, [x1] + + // + // Enable the MMU. Caches will be enabled later, after scatterloading. + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + // + // The Arm Architecture Reference Manual for Armv8-A states: + // + // Instruction accesses to Non-cacheable Normal memory can be held in instruction caches. + // Correspondingly, the sequence for ensuring that modifications to instructions are available + // for execution must include invalidation of the modified locations from the instruction cache, + // even if the instructions are held in Normal Non-cacheable memory. + // This includes cases where the instruction cache is disabled. + // + + dsb ish // ensure all previous stores have completed before invalidating + ic ialluis // I cache invalidate all inner shareable to PoU (which includes secondary cores) + dsb ish // ensure completion on inner shareable domain (which includes secondary cores) + isb + + // Scatter-loading is complete, so enable the caches here, so that the C-library's mutex initialization later will work + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + msr SCTLR_EL1, x1 + isb + + // Zero the bss + ldr x0, =__bss_start__ // Start of block + mov x1, #0 // Fill value + ldr x2, =__bss_end__ // End of block + sub x2, x2, x0 // Length of block + bl memset + + // Set up the standard file handles + bl initialise_monitor_handles + + // Set up _fini and fini_array to be called at exit + ldr x0, =__libc_fini_array + bl atexit + + // Call preinit_array, _init and init_array + bl __libc_init_array + + // Set argc = 1, argv[0] = "" and then call main + .pushsection .data + .align 3 +argv: + .dword arg0 + .dword 0 +arg0: + .byte 0 + .popsection + + mov x0, #1 + ldr x1, =argv + bl main + + b exit // Will not return + +// ------------------------------------------------------------ +// EL1 - secondary CPU init code +// +// This code is run on CPUs 1, 2, 3 etc.... +// ------------------------------------------------------------ + + .global el1_secondary + .type el1_secondary, "function" +el1_secondary: + +wait_for_mmu_ready: + ldr x1, =is_mmu_ready + ldr x1, [x1] + cmp x1, #1 + b.ne wait_for_mmu_ready + + // + // Enable the MMU and caches + // + mrs x1, SCTLR_EL1 + orr x1, x1, #SCTLR_ELx_M + orr x1, x1, #SCTLR_ELx_C + orr x1, x1, #SCTLR_ELx_I + bic x1, x1, #SCTLR_ELx_A // Disable alignment fault checking. To enable, change bic to orr + msr SCTLR_EL1, x1 + isb + + /* EL: Secondary core entrance. */ + B _tx_thread_smp_initialize_wait + +loop_wfi: + dsb SY // Clear all pending data accesses + wfi // Go to sleep + + // + // something woke us from our wait, was it the required interrupt? + // + mov w0, w19 + mov w1, #15 + bl GetPrivateIntPending + cbz w0, loop_wfi + + // + // it was - there's no need to actually take the interrupt, + // so just clear it + // + mov w0, w19 + mov w1, #15 + bl ClearPrivateIntPending + + // + // Branch to thread start + // + //B MainApp + diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/timer_interrupts.c b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/timer_interrupts.c new file mode 100644 index 00000000..8f522217 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/timer_interrupts.c @@ -0,0 +1,152 @@ +/* Bare-metal example for Armv8-A FVP Base model */ + +/* Timer and interrupts */ + +/* Copyright (c) 2016-2018 Arm Limited (or its affiliates). All rights reserved. */ +/* Use, modification and redistribution of this file is subject to your possession of a */ +/* valid End User License Agreement for the Arm Product of which these examples are part of */ +/* and your compliance with all applicable terms and conditions of such licence agreement. */ + +#include + +#include "GICv3.h" +#include "GICv3_gicc.h" +#include "sp804_timer.h" + +void _tx_timer_interrupt(void); + +// LED Base address +#define LED_BASE (volatile unsigned int *)0x1C010008 + + +void nudge_leds(void) // Move LEDs along +{ + static int state = 1; + static int value = 1; + + if (state) + { + int max = (1 << 7); + value <<= 1; + if (value == max) + state = 0; + } + else + { + value >>= 1; + if (value == 1) + state = 1; + } + + *LED_BASE = value; // Update LEDs hardware +} + + +// Initialize Timer 0 and Interrupt Controller +void init_timer(void) +{ + // Enable interrupts + __asm("MSR DAIFClr, #0xF"); + setICC_IGRPEN1_EL1(igrpEnable); + + // Configure the SP804 timer to generate an interrupt + setTimerBaseAddress(0x1C110000); + initTimer(0x200, SP804_AUTORELOAD, SP804_GENERATE_IRQ); + startTimer(); + + // The SP804 timer generates SPI INTID 34. Enable + // this ID, and route it to core 0.0.0.0 (this one!) + SetSPIRoute(34, 0, gicdirouter_ModeSpecific); // Route INTID 34 to 0.0.0.0 (this core) + SetSPIPriority(34, 0); // Set INTID 34 to priority to 0 + ConfigureSPI(34, gicdicfgr_Level); // Set INTID 34 as level-sensitive + EnableSPI(34); // Enable INTID 34 +} + + +// -------------------------------------------------------- + +void irqHandler(void) +{ + unsigned int ID; + + ID = getICC_IAR1(); // readIntAck(); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("irqHandler() - Reserved INTID %d\n\n", ID); + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("irqHandler() - External timer interrupt\n\n"); + nudge_leds(); + clearTimerIrq(); + + /* Call ThreadX timer interrupt processing. */ + _tx_timer_interrupt(); + + break; + + default: + // Unexpected ID value + //printf("irqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} + +// -------------------------------------------------------- + +// Not actually used in this example, but provided for completeness + +void fiqHandler(void) +{ + unsigned int ID; + unsigned int aliased = 0; + + ID = getICC_IAR0(); // readIntAck(); + //printf("fiqHandler() - Read %d from IAR0\n", ID); + + // Check for reserved IDs + if ((1020 <= ID) && (ID <= 1023)) + { + //printf("fiqHandler() - Reserved INTID %d\n\n", ID); + ID = getICC_IAR1(); // readAliasedIntAck(); + //printf("fiqHandler() - Read %d from AIAR\n", ID); + aliased = 1; + + // If still spurious then simply return + if ((1020 <= ID) && (ID <= 1023)) + return; + } + + switch(ID) + { + case 34: + // Dual-Timer 0 (SP804) + //printf("fiqHandler() - External timer interrupt\n\n"); + clearTimerIrq(); + break; + + default: + // Unexpected ID value + //printf("fiqHandler() - Unexpected INTID %d\n\n", ID); + break; + } + + // Write the End of Interrupt register to tell the GIC + // we've finished handling the interrupt + // NOTE: If the ID was read from the Aliased IAR, then + // the aliased EOI register must be used + if (aliased == 0) + setICC_EOIR0(ID); // writeEOI(ID); + else + setICC_EOIR1(ID); // writeAliasedEOI(ID); +} diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds new file mode 100644 index 00000000..6fde52b2 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/use_model_semihosting.ds @@ -0,0 +1 @@ +set semihosting enabled off diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.S b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.S new file mode 100644 index 00000000..45445a98 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.S @@ -0,0 +1,179 @@ +// ------------------------------------------------------------ +// Armv8-A AArch64 - Common helper functions +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + + .global EnableCachesEL1 + .global DisableCachesEL1 + .global InvalidateUDCaches + .global GetMIDR + .global GetMPIDR + .global GetAffinity + .global GetCPUID + +// ------------------------------------------------------------ + +// +// void EnableCachesEL1(void) +// +// enable Instruction and Data caches +// + .type EnableCachesEL1, "function" + .cfi_startproc +EnableCachesEL1: + + mrs x0, SCTLR_EL1 + orr x0, x0, #SCTLR_ELx_I + orr x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + + .type DisableCachesEL1, "function" + .cfi_startproc +DisableCachesEL1: + + mrs x0, SCTLR_EL1 + bic x0, x0, #SCTLR_ELx_I + bic x0, x0, #SCTLR_ELx_C + msr SCTLR_EL1, x0 + + isb + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// void InvalidateUDCaches(void) +// +// Invalidate data and unified caches +// + .type InvalidateUDCaches, "function" + .cfi_startproc +InvalidateUDCaches: + // From the Armv8-A Architecture Reference Manual + + dmb ish // ensure all prior inner-shareable accesses have been observed + + mrs x0, CLIDR_EL1 + and w3, w0, #0x07000000 // get 2 x level of coherence + lsr w3, w3, #23 + cbz w3, finished + mov w10, #0 // w10 = 2 x cache level + mov w8, #1 // w8 = constant 0b1 +loop_level: + add w2, w10, w10, lsr #1 // calculate 3 x cache level + lsr w1, w0, w2 // extract 3-bit cache type for this level + and w1, w1, #0x7 + cmp w1, #2 + b.lt next_level // no data or unified cache at this level + msr CSSELR_EL1, x10 // select this cache level + isb // synchronize change of csselr + mrs x1, CCSIDR_EL1 // read ccsidr + and w2, w1, #7 // w2 = log2(linelen)-4 + add w2, w2, #4 // w2 = log2(linelen) + ubfx w4, w1, #3, #10 // w4 = max way number, right aligned + clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand + lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand + lsl w16, w8, w5 // w16 = amount to decrement way number per iteration +loop_way: + ubfx w7, w1, #13, #15 // w7 = max set number, right aligned + lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand + lsl w17, w8, w2 // w17 = amount to decrement set number per iteration +loop_set: + orr w11, w10, w9 // w11 = combine way number and cache number ... + orr w11, w11, w7 // ... and set number for dc operand + dc isw, x11 // do data cache invalidate by set and way + subs w7, w7, w17 // decrement set number + b.ge loop_set + subs x9, x9, x16 // decrement way number + b.ge loop_way +next_level: + add w10, w10, #2 // increment 2 x cache level + cmp w3, w10 + b.gt loop_level + dsb sy // ensure completion of previous cache maintenance operation + isb +finished: + ret + .cfi_endproc + + +// ------------------------------------------------------------ + +// +// ID Register functions +// + + .type GetMIDR, "function" + .cfi_startproc +GetMIDR: + + mrs x0, MIDR_EL1 + ret + .cfi_endproc + + + .type GetMPIDR, "function" + .cfi_startproc +GetMPIDR: + + mrs x0, MPIDR_EL1 + ret + .cfi_endproc + + + .type GetAffinity, "function" + .cfi_startproc +GetAffinity: + + mrs x0, MPIDR_EL1 + ubfx x1, x0, #32, #8 + bfi w0, w1, #24, #8 + ret + .cfi_endproc + + + .type GetCPUID, "function" + .cfi_startproc +GetCPUID: + + mrs x0, MIDR_EL1 + ubfx x0, x0, #4, #12 // extract PartNum + cmp x0, #0xD0D // Cortex-A77 + b.eq DynamIQ + cmp x0, #0xD0B // Cortex-A76 + b.eq DynamIQ + cmp x0, #0xD0A // Cortex-A75 + b.eq DynamIQ + cmp x0, #0xD05 // Cortex-A55 + b.eq DynamIQ + b Others +DynamIQ: + mrs x0, MPIDR_EL1 + ubfx x0, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + ret + +Others: + mrs x0, MPIDR_EL1 + ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH + ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH + add x0, x1, x2, LSL #2 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.h new file mode 100644 index 00000000..b09079a4 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_aarch64.h @@ -0,0 +1,103 @@ +/* + * + * Armv8-A AArch64 common helper functions + * + * Copyright (c) 2012-2014 Arm Limited (or its affiliates). All rights reserved. + * Use, modification and redistribution of this file is subject to your possession of a + * valid End User License Agreement for the Arm Product of which these examples are part of + * and your compliance with all applicable terms and conditions of such licence agreement. + */ + +#ifndef V8_AARCH64_H +#define V8_AARCH64_H + +/* + * Parameters for data barriers + */ +#define OSHLD 1 +#define OSHST 2 +#define OSH 3 +#define NSHLD 5 +#define NSHST 6 +#define NSH 7 +#define ISHLD 9 +#define ISHST 10 +#define ISH 11 +#define LD 13 +#define ST 14 +#define SY 15 + +/**********************************************************************/ + +/* + * function prototypes + */ + +/* + * void InvalidateUDCaches(void) + * invalidates all Unified and Data Caches + * + * Inputs + * + * + * Returns + * + * + * Side Effects + * guarantees that all levels of cache will be invalidated before + * returning to caller + */ +void InvalidateUDCaches(void); + +/* + * unsigned long long EnableCachesEL1(void) + * enables I- and D- caches at EL1 + * + * Inputs + * + * + * Returns + * New value of SCTLR_EL1 + * + * Side Effects + * context will be synchronised before returning to caller + */ +unsigned long long EnableCachesEL1(void); + +/* + * unsigned long long GetMIDR(void) + * returns the contents of MIDR_EL0 + * + * Inputs + * + * + * Returns + * MIDR_EL0 + */ +unsigned long long GetMIDR(void); + +/* + * unsigned long long GetMPIDR(void) + * returns the contents of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0 + */ +unsigned long long GetMPIDR(void); + +/* + * unsigned int GetCPUID(void) + * returns the Aff0 field of MPIDR_EL0 + * + * Inputs + * + * + * Returns + * MPIDR_EL0[7:0] + */ +unsigned int GetCPUID(void); + +#endif diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_mmu.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_mmu.h new file mode 100644 index 00000000..bce62b54 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_mmu.h @@ -0,0 +1,128 @@ +// +// Defines for v8 Memory Model +// +// Copyright (c) 2012-2019 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_MMU_H +#define V8_MMU_H + +// +// Translation Control Register fields +// +// RGN field encodings +// +#define TCR_RGN_NC 0b00 +#define TCR_RGN_WBWA 0b01 +#define TCR_RGN_WT 0b10 +#define TCR_RGN_WBRA 0b11 + +// +// Shareability encodings +// +#define TCR_SHARE_NONE 0b00 +#define TCR_SHARE_OUTER 0b10 +#define TCR_SHARE_INNER 0b11 + +// +// Granule size encodings +// +#define TCR_GRANULE_4K 0b00 +#define TCR_GRANULE_64K 0b01 +#define TCR_GRANULE_16K 0b10 + +// +// Physical Address sizes +// +#define TCR_SIZE_4G 0b000 +#define TCR_SIZE_64G 0b001 +#define TCR_SIZE_1T 0b010 +#define TCR_SIZE_4T 0b011 +#define TCR_SIZE_16T 0b100 +#define TCR_SIZE_256T 0b101 + +// +// Translation Control Register fields +// +#define TCR_EL1_T0SZ_SHIFT 0 +#define TCR_EL1_EPD0 (1 << 7) +#define TCR_EL1_IRGN0_SHIFT 8 +#define TCR_EL1_ORGN0_SHIFT 10 +#define TCR_EL1_SH0_SHIFT 12 +#define TCR_EL1_TG0_SHIFT 14 + +#define TCR_EL1_T1SZ_SHIFT 16 +#define TCR_EL1_A1 (1 << 22) +#define TCR_EL1_EPD1 (1 << 23) +#define TCR_EL1_IRGN1_SHIFT 24 +#define TCR_EL1_ORGN1_SHIFT 26 +#define TCR_EL1_SH1_SHIFT 28 +#define TCR_EL1_TG1_SHIFT 30 +#define TCR_EL1_IPS_SHIFT 32 +#define TCR_EL1_AS (1 << 36) +#define TCR_EL1_TBI0 (1 << 37) +#define TCR_EL1_TBI1 (1 << 38) + +// +// Stage 1 Translation Table descriptor fields +// +#define TT_S1_ATTR_FAULT (0b00 << 0) +#define TT_S1_ATTR_BLOCK (0b01 << 0) // Level 1/2 +#define TT_S1_ATTR_TABLE (0b11 << 0) // Level 0/1/2 +#define TT_S1_ATTR_PAGE (0b11 << 0) // Level 3 + +#define TT_S1_ATTR_MATTR_LSB 2 + +#define TT_S1_ATTR_NS (1 << 5) + +#define TT_S1_ATTR_AP_RW_PL1 (0b00 << 6) +#define TT_S1_ATTR_AP_RW_ANY (0b01 << 6) +#define TT_S1_ATTR_AP_RO_PL1 (0b10 << 6) +#define TT_S1_ATTR_AP_RO_ANY (0b11 << 6) + +#define TT_S1_ATTR_SH_NONE (0b00 << 8) +#define TT_S1_ATTR_SH_OUTER (0b10 << 8) +#define TT_S1_ATTR_SH_INNER (0b11 << 8) + +#define TT_S1_ATTR_AF (1 << 10) +#define TT_S1_ATTR_nG (1 << 11) + +// OA bits [15:12] - If Armv8.2-LPA is implemented, bits[15:12] are bits[51:48] +// and bits[47:16] are bits[47:16] of the output address for a page of memory + +#define TT_S1_ATTR_nT (1 << 16) // Present if Armv8.4-TTRem is implemented, otherwise RES0 + +#define TT_S1_ATTR_DBM (1 << 51) // Present if Armv8.1-TTHM is implemented, otherwise RES0 + +#define TT_S1_ATTR_CONTIG (1 << 52) +#define TT_S1_ATTR_PXN (1 << 53) +#define TT_S1_ATTR_UXN (1 << 54) + +// PBHA bits[62:59] - If Armv8.2-TTPBHA is implemented, hardware can use these bits +// for IMPLEMENTATIONDEFINED purposes, otherwise IGNORED + +#define TT_S1_MAIR_DEV_nGnRnE 0b00000000 +#define TT_S1_MAIR_DEV_nGnRE 0b00000100 +#define TT_S1_MAIR_DEV_nGRE 0b00001000 +#define TT_S1_MAIR_DEV_GRE 0b00001100 + +// +// Inner and Outer Normal memory attributes use the same bit patterns +// Outer attributes just need to be shifted up +// +#define TT_S1_MAIR_OUTER_SHIFT 4 + +#define TT_S1_MAIR_WT_TRANS_RA 0b0010 + +#define TT_S1_MAIR_WB_TRANS_RA 0b0110 +#define TT_S1_MAIR_WB_TRANS_RWA 0b0111 + +#define TT_S1_MAIR_WT_RA 0b1010 + +#define TT_S1_MAIR_WB_RA 0b1110 +#define TT_S1_MAIR_WB_RWA 0b1111 + +#endif // V8_MMU_H diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_system.h b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_system.h new file mode 100644 index 00000000..a62d2a33 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_system.h @@ -0,0 +1,115 @@ +// +// Defines for v8 System Registers +// +// Copyright (c) 2012-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#ifndef V8_SYSTEM_H +#define V8_SYSTEM_H + +// +// AArch64 SPSR +// +#define AARCH64_SPSR_EL3h 0b1101 +#define AARCH64_SPSR_EL3t 0b1100 +#define AARCH64_SPSR_EL2h 0b1001 +#define AARCH64_SPSR_EL2t 0b1000 +#define AARCH64_SPSR_EL1h 0b0101 +#define AARCH64_SPSR_EL1t 0b0100 +#define AARCH64_SPSR_EL0t 0b0000 +#define AARCH64_SPSR_RW (1 << 4) +#define AARCH64_SPSR_F (1 << 6) +#define AARCH64_SPSR_I (1 << 7) +#define AARCH64_SPSR_A (1 << 8) +#define AARCH64_SPSR_D (1 << 9) +#define AARCH64_SPSR_IL (1 << 20) +#define AARCH64_SPSR_SS (1 << 21) +#define AARCH64_SPSR_V (1 << 28) +#define AARCH64_SPSR_C (1 << 29) +#define AARCH64_SPSR_Z (1 << 30) +#define AARCH64_SPSR_N (1 << 31) + +// +// Multiprocessor Affinity Register +// +#define MPIDR_EL1_AFF3_LSB 32 +#define MPIDR_EL1_U (1 << 30) +#define MPIDR_EL1_MT (1 << 24) +#define MPIDR_EL1_AFF2_LSB 16 +#define MPIDR_EL1_AFF1_LSB 8 +#define MPIDR_EL1_AFF0_LSB 0 +#define MPIDR_EL1_AFF_WIDTH 8 + +// +// Data Cache Zero ID Register +// +#define DCZID_EL0_BS_LSB 0 +#define DCZID_EL0_BS_WIDTH 4 +#define DCZID_EL0_DZP_LSB 5 +#define DCZID_EL0_DZP (1 << 5) + +// +// System Control Register +// +#define SCTLR_EL1_UCI (1 << 26) +#define SCTLR_ELx_EE (1 << 25) +#define SCTLR_EL1_E0E (1 << 24) +#define SCTLR_ELx_WXN (1 << 19) +#define SCTLR_EL1_nTWE (1 << 18) +#define SCTLR_EL1_nTWI (1 << 16) +#define SCTLR_EL1_UCT (1 << 15) +#define SCTLR_EL1_DZE (1 << 14) +#define SCTLR_ELx_I (1 << 12) +#define SCTLR_EL1_UMA (1 << 9) +#define SCTLR_EL1_SED (1 << 8) +#define SCTLR_EL1_ITD (1 << 7) +#define SCTLR_EL1_THEE (1 << 6) +#define SCTLR_EL1_CP15BEN (1 << 5) +#define SCTLR_EL1_SA0 (1 << 4) +#define SCTLR_ELx_SA (1 << 3) +#define SCTLR_ELx_C (1 << 2) +#define SCTLR_ELx_A (1 << 1) +#define SCTLR_ELx_M (1 << 0) + +// +// Architectural Feature Access Control Register +// +#define CPACR_EL1_TTA (1 << 28) +#define CPACR_EL1_FPEN (3 << 20) + +// +// Architectural Feature Trap Register +// +#define CPTR_ELx_TCPAC (1 << 31) +#define CPTR_ELx_TTA (1 << 20) +#define CPTR_ELx_TFP (1 << 10) + +// +// Secure Configuration Register +// +#define SCR_EL3_TWE (1 << 13) +#define SCR_EL3_TWI (1 << 12) +#define SCR_EL3_ST (1 << 11) +#define SCR_EL3_RW (1 << 10) +#define SCR_EL3_SIF (1 << 9) +#define SCR_EL3_HCE (1 << 8) +#define SCR_EL3_SMD (1 << 7) +#define SCR_EL3_EA (1 << 3) +#define SCR_EL3_FIQ (1 << 2) +#define SCR_EL3_IRQ (1 << 1) +#define SCR_EL3_NS (1 << 0) + +// +// Hypervisor Configuration Register +// +#define HCR_EL2_ID (1 << 33) +#define HCR_EL2_CD (1 << 32) +#define HCR_EL2_RW (1 << 31) +#define HCR_EL2_TRVM (1 << 30) +#define HCR_EL2_HVC (1 << 29) +#define HCR_EL2_TDZ (1 << 28) + +#endif // V8_SYSTEM_H diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_utils.S b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_utils.S new file mode 100644 index 00000000..888892a0 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/v8_utils.S @@ -0,0 +1,69 @@ +// +// Simple utility routines for baremetal v8 code +// +// Copyright (c) 2013-2017 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// + +#include "v8_system.h" + + .text + .cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame + +// +// void *ZeroBlock(void *blockPtr, unsigned int nBytes) +// +// Zero fill a block of memory +// Fill memory pages or similar structures with zeros. +// The byte count must be a multiple of the block fill size (16 bytes) +// +// Inputs: +// blockPtr - base address of block to fill +// nBytes - block size, in bytes +// +// Returns: +// pointer to just filled block, NULL if nBytes is +// incompatible with block fill size +// + .global ZeroBlock + .type ZeroBlock, "function" + .cfi_startproc +ZeroBlock: + + // + // we fill data by steam, 16 bytes at a time: check that + // blocksize is a multiple of that + // + ubfx x2, x1, #0, #4 + cbnz x2, incompatible + + // + // we already have one register full of zeros, get another + // + mov x3, x2 + + // + // OK, set temporary pointer and away we go + // + add x0, x0, x1 + +loop0: + subs x1, x1, #16 + stp x2, x3, [x0, #-16]! + b.ne loop0 + + // + // that's all - x0 will be back to its start value + // + ret + + // + // parameters are incompatible with block size - return + // an indication that this is so + // +incompatible: + mov x0,#0 + ret + .cfi_endproc diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/vectors.S b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/vectors.S new file mode 100644 index 00000000..7784f98e --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/sample_threadx/vectors.S @@ -0,0 +1,252 @@ +// ------------------------------------------------------------ +// Armv8-A Vector tables +// +// Copyright (c) 2014-2016 Arm Limited (or its affiliates). All rights reserved. +// Use, modification and redistribution of this file is subject to your possession of a +// valid End User License Agreement for the Arm Product of which these examples are part of +// and your compliance with all applicable terms and conditions of such licence agreement. +// ------------------------------------------------------------ + + + .global el1_vectors + .global el2_vectors + .global el3_vectors + .global c0sync1 + .global irqHandler + .global fiqHandler + .global irqFirstLevelHandler + .global fiqFirstLevelHandler + + .section EL1VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el1_vectors: +c0sync1: B c0sync1 + + .balign 0x80 +c0irq1: B irqFirstLevelHandler + + .balign 0x80 +c0fiq1: B fiqFirstLevelHandler + + .balign 0x80 +c0serr1: B c0serr1 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync1: B cxsync1 + + .balign 0x80 +cxirq1: B irqFirstLevelHandler + + .balign 0x80 +cxfiq1: B fiqFirstLevelHandler + + .balign 0x80 +cxserr1: B cxserr1 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync1: B l64sync1 + + .balign 0x80 +l64irq1: B irqFirstLevelHandler + + .balign 0x80 +l64fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l64serr1: B l64serr1 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync1: B l32sync1 + + .balign 0x80 +l32irq1: B irqFirstLevelHandler + + .balign 0x80 +l32fiq1: B fiqFirstLevelHandler + + .balign 0x80 +l32serr1: B l32serr1 + +//---------------------------------------------------------------- + + .section EL2VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el2_vectors: +c0sync2: B c0sync2 + + .balign 0x80 +c0irq2: B irqFirstLevelHandler + + .balign 0x80 +c0fiq2: B fiqFirstLevelHandler + + .balign 0x80 +c0serr2: B c0serr2 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync2: B cxsync2 + + .balign 0x80 +cxirq2: B irqFirstLevelHandler + + .balign 0x80 +cxfiq2: B fiqFirstLevelHandler + + .balign 0x80 +cxserr2: B cxserr2 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync2: B l64sync2 + + .balign 0x80 +l64irq2: B irqFirstLevelHandler + + .balign 0x80 +l64fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l64serr2: B l64serr2 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync2: B l32sync2 + + .balign 0x80 +l32irq2: B irqFirstLevelHandler + + .balign 0x80 +l32fiq2: B fiqFirstLevelHandler + + .balign 0x80 +l32serr2: B l32serr2 + +//---------------------------------------------------------------- + + .section EL3VECTORS, "ax" + .align 11 + +// +// Current EL with SP0 +// +el3_vectors: +c0sync3: B c0sync3 + + .balign 0x80 +c0irq3: B irqFirstLevelHandler + + .balign 0x80 +c0fiq3: B fiqFirstLevelHandler + + .balign 0x80 +c0serr3: B c0serr3 + +// +// Current EL with SPx +// + .balign 0x80 +cxsync3: B cxsync3 + + .balign 0x80 +cxirq3: B irqFirstLevelHandler + + .balign 0x80 +cxfiq3: B fiqFirstLevelHandler + + .balign 0x80 +cxserr3: B cxserr3 + +// +// Lower EL using AArch64 +// + .balign 0x80 +l64sync3: B l64sync3 + + .balign 0x80 +l64irq3: B irqFirstLevelHandler + + .balign 0x80 +l64fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l64serr3: B l64serr3 + +// +// Lower EL using AArch32 +// + .balign 0x80 +l32sync3: B l32sync3 + + .balign 0x80 +l32irq3: B irqFirstLevelHandler + + .balign 0x80 +l32fiq3: B fiqFirstLevelHandler + + .balign 0x80 +l32serr3: B l32serr3 + + + .section InterruptHandlers, "ax" + .balign 4 + + .type irqFirstLevelHandler, "function" +irqFirstLevelHandler: + MSR SPSel, 0 + STP x29, x30, [sp, #-16]! + BL _tx_thread_context_save + BL irqHandler + B _tx_thread_context_restore + + .type fiqFirstLevelHandler, "function" +fiqFirstLevelHandler: + STP x29, x30, [sp, #-16]! + STP x18, x19, [sp, #-16]! + STP x16, x17, [sp, #-16]! + STP x14, x15, [sp, #-16]! + STP x12, x13, [sp, #-16]! + STP x10, x11, [sp, #-16]! + STP x8, x9, [sp, #-16]! + STP x6, x7, [sp, #-16]! + STP x4, x5, [sp, #-16]! + STP x2, x3, [sp, #-16]! + STP x0, x1, [sp, #-16]! + + BL fiqHandler + + LDP x0, x1, [sp], #16 + LDP x2, x3, [sp], #16 + LDP x4, x5, [sp], #16 + LDP x6, x7, [sp], #16 + LDP x8, x9, [sp], #16 + LDP x10, x11, [sp], #16 + LDP x12, x13, [sp], #16 + LDP x14, x15, [sp], #16 + LDP x16, x17, [sp], #16 + LDP x18, x19, [sp], #16 + LDP x29, x30, [sp], #16 + ERET diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/tx/.cproject b/ports_smp/cortex_a78_smp/gnu/example_build/tx/.cproject new file mode 100644 index 00000000..e8013d86 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/tx/.cproject @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ports_smp/cortex_a78_smp/gnu/example_build/tx/.project b/ports_smp/cortex_a78_smp/gnu/example_build/tx/.project new file mode 100644 index 00000000..10681969 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/example_build/tx/.project @@ -0,0 +1,48 @@ + + + tx + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + inc_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/inc + + + inc_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/inc + + + src_generic + 2 + $%7BPARENT-5-PROJECT_LOC%7D/common_smp/src + + + src_port + 2 + $%7BPARENT-2-PROJECT_LOC%7D/src + + + diff --git a/ports_smp/cortex_a78_smp/gnu/inc/tx_port.h b/ports_smp/cortex_a78_smp/gnu/inc/tx_port.h new file mode 100644 index 00000000..fcac1485 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/inc/tx_port.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h ARMv8-A-SMP */ +/* 6.1.10 */ +/* */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Bhupendra Naphade Modified comment(s),updated */ +/* macro definition, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/************* Define ThreadX SMP constants. *************/ + +/* Define the ThreadX SMP maximum number of cores. */ + +#ifndef TX_THREAD_SMP_MAX_CORES +#define TX_THREAD_SMP_MAX_CORES 4 +#endif + + +/* Define the ThreadX SMP core mask. */ + +#ifndef TX_THREAD_SMP_CORE_MASK +#define TX_THREAD_SMP_CORE_MASK 0xF /* Where bit 0 represents Core 0, bit 1 represents Core 1, etc. */ +#endif + + +/* Define INLINE_DECLARE to whitespace for ARM compiler. */ + +#define INLINE_DECLARE + + +/* Define ThreadX SMP initialization macro. */ + +#define TX_PORT_SPECIFIC_PRE_INITIALIZATION + + +/* Define ThreadX SMP pre-scheduler initialization. */ + +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION + + +/* Enable the inter-core interrupt logic. */ + +#define TX_THREAD_SMP_INTER_CORE_INTERRUPT + + +/* Determine if there is customer-specific wakeup logic needed. */ + +#ifdef TX_THREAD_SMP_WAKEUP_LOGIC + +/* Include customer-specific wakeup code. */ + +#include "tx_thread_smp_core_wakeup.h" +#else + +#ifdef TX_THREAD_SMP_DEFAULT_WAKEUP_LOGIC + +/* Default wakeup code. */ +#define TX_THREAD_SMP_WAKEUP_LOGIC +#define TX_THREAD_SMP_WAKEUP(i) _tx_thread_smp_core_preempt(i) +#endif +#endif + + +/* Ensure that the in-line resume/suspend define is not allowed. */ + +#ifdef TX_INLINE_THREAD_RESUME_SUSPEND +#undef TX_INLINE_THREAD_RESUME_SUSPEND +#endif + + +/************* End ThreadX SMP constants. *************/ + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Override the alignment type to use 64-bit alignment and storage for pointers. */ + +#define ALIGN_TYPE_DEFINED +typedef unsigned long long ALIGN_TYPE; + + +/* Override the free block marker for byte pools to be a 64-bit constant. */ + +#define TX_BYTE_BLOCK_FREE ((ALIGN_TYPE) 0xFFFFEEEEFFFFEEEE) + + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 4096 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + + +/* Define various constants for the ThreadX ARM port. */ + +#define TX_INT_DISABLE 0xC0 /* Disable IRQ & FIQ interrupts */ +#define TX_INT_ENABLE 0x00 /* Enable IRQ & FIQ interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE _tx_thread_smp_time_get() +#endif +#else +#ifndef TX_TRACE_TIME_SOURCE +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif +#endif +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#ifdef TX_ENABLE_FIQ_SUPPORT +#define TX_FIQ_ENABLED 1 +#else +#define TX_FIQ_ENABLED 0 +#endif + +#ifdef TX_ENABLE_IRQ_NESTING +#define TX_IRQ_NESTING_ENABLED 2 +#else +#define TX_IRQ_NESTING_ENABLED 0 +#endif + +#ifdef TX_ENABLE_FIQ_NESTING +#define TX_FIQ_NESTING_ENABLED 4 +#else +#define TX_FIQ_NESTING_ENABLED 0 +#endif + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (TX_FIQ_ENABLED | TX_IRQ_NESTING_ENABLED | TX_FIQ_NESTING_ENABLED) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#define TX_THREAD_EXTENSION_2 ULONG tx_thread_fp_enable; +#define TX_THREAD_EXTENSION_3 VOID *tx_thread_extension_ptr; + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Determine if the ARM architecture has the CLZ instruction. This is available on + architectures v5 and above. If available, redefine the macro for calculating the + lowest bit set. */ + +#ifndef TX_DISABLE_INLINE + +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) b = (UINT) __builtin_ctz((unsigned int) m); + +#endif + + +/* Define the internal timer extension to also hold the thread pointer such that _tx_thread_timeout + can figure out what thread timeout to process. */ + +#define TX_TIMER_INTERNAL_EXTENSION VOID *tx_timer_internal_extension_ptr; + + +/* Define the thread timeout setup logic in _tx_thread_create. */ + +#define TX_THREAD_CREATE_TIMEOUT_SETUP(t) (t) -> tx_thread_timer.tx_timer_internal_timeout_function = &(_tx_thread_timeout); \ + (t) -> tx_thread_timer.tx_timer_internal_timeout_param = 0; \ + (t) -> tx_thread_timer.tx_timer_internal_extension_ptr = (VOID *) (t); + + +/* Define the thread timeout pointer setup in _tx_thread_timeout. */ + +#define TX_THREAD_TIMEOUT_POINTER_SETUP(t) (t) = (TX_THREAD *) _tx_timer_expired_timer_ptr -> tx_timer_internal_extension_ptr; + + +/************* Define ThreadX SMP data types and function prototypes. *************/ + +struct TX_THREAD_STRUCT; + + +/* Define the ThreadX SMP protection structure. */ + +typedef struct TX_THREAD_SMP_PROTECT_STRUCT +{ + ULONG tx_thread_smp_protect_in_force; + ULONG tx_thread_smp_protect_core; + ULONG tx_thread_smp_protect_count; + ULONG tx_thread_smp_protect_pad_0; + ULONG tx_thread_smp_protect_pad_1; + ULONG tx_thread_smp_protect_pad_2; + ULONG tx_thread_smp_protect_pad_3; +} TX_THREAD_SMP_PROTECT; + + +/* Define ThreadX interrupt lockout and restore macros for protection on + access of critical kernel information. The restore interrupt macro must + restore the interrupt posture of the running thread prior to the value + present prior to the disable macro. In most cases, the save area macro + is used to define a local function save area for the disable and restore + macros. */ + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_smp_protect(); +#define TX_RESTORE _tx_thread_smp_unprotect(interrupt_save); + +/************* End ThreadX SMP data type and function prototype definitions. *************/ + + +/* Define the interrupt lockout macros for each ThreadX object. */ + +#define TX_BLOCK_POOL_DISABLE TX_DISABLE +#define TX_BYTE_POOL_DISABLE TX_DISABLE +#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE +#define TX_MUTEX_DISABLE TX_DISABLE +#define TX_QUEUE_DISABLE TX_DISABLE +#define TX_SEMAPHORE_DISABLE TX_DISABLE + + +/* Define FP extension for ARMv8. Each is assumed to be called in the context of the executing thread. */ + +#ifndef TX_SOURCE_CODE +#define tx_thread_fp_enable _tx_thread_fp_enable +#define tx_thread_fp_disable _tx_thread_fp_disable +#endif + +VOID tx_thread_fp_enable(VOID); +VOID tx_thread_fp_disable(VOID); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) Microsoft Corporation. All rights reserved. * ThreadX ARMv8-A-SMP Version 6.1.10 *"; +#else +extern CHAR _tx_version_id[]; +#endif + + +#endif diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_initialize_low_level.S b/ports_smp/cortex_a78_smp/gnu/src/tx_initialize_low_level.S new file mode 100644 index 00000000..0c377cac --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_initialize_low_level.S @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, 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 */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .global _tx_initialize_low_level + .type _tx_initialize_low_level, @function +_tx_initialize_low_level: + + MSR DAIFSet, 0x3 // Lockout interrupts + + + /* Save the system stack pointer. */ + // _tx_thread_system_stack_ptr = (VOID_PTR) (sp); + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0] // Store system stack + + /* Save the first available memory address. */ + // _tx_initialize_unused_memory = (VOID_PTR) __top_of_ram; + + LDR x0, =_tx_initialize_unused_memory // Pickup address of unused memory ptr + LDR x1, =__top_of_ram // Pickup unused memory address - A free + // memory section must be setup after the + // heap section. + STR x1, [x0] // Store unused memory address + + /* Done, return to caller. */ + + RET // Return to caller +// } + + .align 3 + diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_restore.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000..4df471ac --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,390 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function restores the interrupt context if it is processing a */ +/* nested interrupt. If not, it returns to the interrupt thread if no */ +/* preemption is necessary. Otherwise, if preemption is necessary or */ +/* if no thread was running, the function returns to the scheduler. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling routine */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .type _tx_thread_context_restore, @function +_tx_thread_context_restore: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR exit function to indicate an ISR is complete. */ + + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + + /* Pickup the CPU ID. */ + + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x2, LSL #2 // Calculate CPU ID +#endif + + /* Determine if interrupts are nested. */ + // if (--_tx_thread_system_state) + // { + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x8, LSL #2] // Pickup system state + SUB w2, w2, #1 // Decrement the counter + STR w2, [x3, x8, LSL #2] // Store the counter + CMP w2, #0 // Was this the first interrupt? + BEQ __tx_thread_not_nested_restore // If so, not a nested restore + + /* Interrupts are nested. */ + + /* Just recover the saved registers and return to the point of + interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } +__tx_thread_not_nested_restore: + + /* Determine if a thread was interrupted and no preemption is required. */ + // else if (((_tx_thread_current_ptr) && (_tx_thread_current_ptr == _tx_thread_execute_ptr) + // || (_tx_thread_preempt_disable)) + // { + + LDR x1, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x1, x8, LSL #3] // Pickup actual current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_restore // Yes, idle system was interrupted + LDR x3, =_tx_thread_execute_ptr // Pickup address of execute thread ptr + LDR x2, [x3, x8, LSL #3] // Pickup actual execute thread pointer + CMP x0, x2 // Is the same thread highest priority? + BEQ __tx_thread_no_preempt_restore // Same thread in the execute list, + // no preemption needs to happen + LDR x3, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x3, #4] // Pickup the owning core + CMP w3, w8 // Is it this core? + BNE __tx_thread_preempt_restore // No, proceed to preempt thread + + LDR x3, =_tx_thread_preempt_disable // Pickup preempt disable address + LDR w2, [x3, #0] // Pickup actual preempt disable flag + CMP w2, #0 // Is it set? + BEQ __tx_thread_preempt_restore // No, okay to preempt this thread + +__tx_thread_no_preempt_restore: + + /* Restore interrupted thread or ISR. */ + + /* Pickup the saved stack pointer. */ + // sp = _tx_thread_current_ptr -> tx_thread_stack_ptr; + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + /* Recover the saved context and return to the point of interrupt. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + + // } + // else + // { +__tx_thread_preempt_restore: + + /* Was the thread being preempted waiting for the lock? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] != 0) + // { + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + CMP w3, #0 + BEQ _nobody_waiting_for_lock // Is the core waiting for the lock? + + /* Do we not have the lock? This means the ISR never got the inter-core lock. */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_owned != this_core) + // { + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w8, w3 // Compare our core to the owning core + BEQ _this_core_has_lock // Do we have the lock? + + /* We don't have the lock. This core should be in the list. Remove it. */ + // _tx_thread_smp_protect_wait_list_remove(this_core); + + _tx_thread_smp_protect_wait_list_remove // Call macro to remove core from the list + B _nobody_waiting_for_lock // Leave + + // } + // else + // { + /* We have the lock. This means the ISR got the inter-core lock, but + never released it because it saw that there was someone waiting. + Note this core is not in the list. */ + +_this_core_has_lock: + + /* We're no longer waiting. Note that this should be zero since this happens during thread preemption. */ + // _tx_thread_smp_protect_wait_counts[core]--; + + LDR x2, =_tx_thread_smp_protect_wait_counts // Load waiting count list + LDR w3, [x2, x8, LSL #2] // Load waiting value for this core + SUB w3, w3, #1 // Decrement waiting value. Should be zero now + STR w3, [x2, x8, LSL #2] // Store new waiting value + + /* Now release the inter-core lock. */ + + /* Set protected core as invalid. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = 0xFFFFFFFF; + + LDR x2, =_tx_thread_smp_protection // Load address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release protection. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 0; + + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + + /* Wake up waiting processors. Note interrupts are already enabled. */ + +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs +#endif + + // } + // } + +_nobody_waiting_for_lock: + + LDR x4, [x0, #8] // Switch to thread stack pointer + MOV sp, x4 // + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + STP x20, x21, [sp, #-16]! // Save x20, x21 + STP x22, x23, [sp, #-16]! // Save x22, x23 + STP x24, x25, [sp, #-16]! // Save x24, x25 + STP x26, x27, [sp, #-16]! // Save x26, x27 + STP x28, x29, [sp, #-16]! // Save x28, x29 +#ifdef ENABLE_ARM_FP + LDR w3, [x0, #268] // Pickup FP enable flag + CMP w3, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q0, q1, [sp, #-32]! // Save q0, q1 + STP q2, q3, [sp, #-32]! // Save q2, q3 + STP q4, q5, [sp, #-32]! // Save q4, q5 + STP q6, q7, [sp, #-32]! // Save q6, q7 + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + STP q16, q17, [sp, #-32]! // Save q16, q17 + STP q18, q19, [sp, #-32]! // Save q18, q19 + STP q20, q21, [sp, #-32]! // Save q20, q21 + STP q22, q23, [sp, #-32]! // Save q22, q23 + STP q24, q25, [sp, #-32]! // Save q24, q25 + STP q26, q27, [sp, #-32]! // Save q26, q27 + STP q28, q29, [sp, #-32]! // Save q28, q29 + STP q30, q31, [sp, #-32]! // Save q30, q31 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + STP x4, x5, [sp, #-16]! // Save x4 (SPSR_EL3), x5 (ELR_E3) + + MOV x3, sp // Move sp into x3 + STR x3, [x0, #8] // Save stack pointer in thread control + // block + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + + /* Save the remaining time-slice and disable it. */ + // if (_tx_timer_time_slice) + // { + + LDR x3, =_tx_timer_time_slice // Pickup time-slice variable address + LDR w2, [x3, x8, LSL #2] // Pickup time-slice + CMP w2, #0 // Is it active? + BEQ __tx_thread_dont_save_ts // No, don't save it + + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w2, [x0, #36] // Save thread's time-slice + MOV w2, #0 // Clear value + STR w2, [x3, x8, LSL #2] // Disable global time-slice flag + + // } +__tx_thread_dont_save_ts: + + + /* Clear the current task pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + MOV x2, #0 // NULL value + STR x2, [x1, x8, LSL #3] // Clear current thread pointer + + /* Set bit indicating this thread is ready for execution. */ + + MOV x2, #1 // Build ready flag + STR w2, [x0, #260] // Set thread's ready flag + DMB ISH // Ensure that accesses to shared resource have completed + + /* Return to the scheduler. */ + // _tx_thread_schedule(); + + // } + +__tx_thread_idle_system_restore: + + /* Just return back to the scheduler! */ + + LDR x1, =_tx_thread_schedule // Build address for _tx_thread_schedule +#ifdef EL1 + MSR ELR_EL1, x1 // Setup point of interrupt +// MOV x1, #0x4 // Setup EL1 return +// MSR spsr_el1, x1 // Move into SPSR +#else +#ifdef EL2 + MSR ELR_EL2, x1 // Setup point of interrupt +// MOV x1, #0x8 // Setup EL2 return +// MSR spsr_el2, x1 // Move into SPSR +#else + MSR ELR_EL3, x1 // Setup point of interrupt +// MOV x1, #0xC // Setup EL3 return +// MSR spsr_el3, x1 // Move into SPSR +#endif +#endif + ERET // Return to scheduler +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_save.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000..79e33086 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_context_save.S @@ -0,0 +1,254 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function saves the context of an executing thread in the */ +/* beginning of interrupt processing. The function also ensures that */ +/* the system stack is used upon return to the calling ISR. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .type _tx_thread_context_save, @function +_tx_thread_context_save: + + /* Upon entry to this routine, it is assumed that IRQ/FIQ interrupts are locked + out, x29 (frame pointer), x30 (link register) are saved, we are in the proper EL, + and all other registers are intact. */ + + /* Check for a nested interrupt condition. */ + // if (_tx_thread_system_state++) + // { + + STP x0, x1, [sp, #-16]! // Save x0, x1 + STP x2, x3, [sp, #-16]! // Save x2, x3 + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x3, =_tx_thread_system_state // Pickup address of system state var + LDR w2, [x3, x1, LSL #2] // Pickup system state + CMP w2, #0 // Is this the first interrupt? + BEQ __tx_thread_not_nested_save // Yes, not a nested context save + + /* Nested interrupt condition. */ + + ADD w2, w2, #1 // Increment the nested interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + + /* Save the rest of the scratch registers on the stack and return to the + calling ISR. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x0, SPSR_EL1 // Pickup SPSR + MRS x1, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x0, SPSR_EL2 // Pickup SPSR + MRS x1, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x0, SPSR_EL3 // Pickup SPSR + MRS x1, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x0, x1, [sp, #-16]! // Save SPSR, ELR + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + /* Return to the ISR. */ + + RET // Return to ISR + +__tx_thread_not_nested_save: + // } + + /* Otherwise, not nested, check to see if a thread was running. */ + // else if (_tx_thread_current_ptr) + // { + + ADD w2, w2, #1 // Increment the interrupt counter + STR w2, [x3, x1, LSL #2] // Store it back in the variable + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread ptr + LDR x0, [x2, x1, LSL #3] // Pickup current thread pointer + CMP x0, #0 // Is it NULL? + BEQ __tx_thread_idle_system_save // If so, interrupt occurred in + // scheduling loop - nothing needs saving! + + /* Save minimal context of interrupted thread. */ + + STP x4, x5, [sp, #-16]! // Save x4, x5 + STP x6, x7, [sp, #-16]! // Save x6, x7 + STP x8, x9, [sp, #-16]! // Save x8, x9 + STP x10, x11, [sp, #-16]! // Save x10, x11 + STP x12, x13, [sp, #-16]! // Save x12, x13 + STP x14, x15, [sp, #-16]! // Save x14, x15 + STP x16, x17, [sp, #-16]! // Save x16, x17 + STP x18, x19, [sp, #-16]! // Save x18, x19 +#ifdef EL1 + MRS x4, SPSR_EL1 // Pickup SPSR + MRS x5, ELR_EL1 // Pickup ELR (point of interrupt) +#else +#ifdef EL2 + MRS x4, SPSR_EL2 // Pickup SPSR + MRS x5, ELR_EL2 // Pickup ELR (point of interrupt) +#else + MRS x4, SPSR_EL3 // Pickup SPSR + MRS x5, ELR_EL3 // Pickup ELR (point of interrupt) +#endif +#endif + STP x4, x5, [sp, #-16]! // Save SPSR, ELR + + /* Save the current stack pointer in the thread's control block. */ + // _tx_thread_current_ptr -> tx_thread_stack_ptr = sp; + + MOV x4, sp // + STR x4, [x0, #8] // Save thread stack pointer + + /* Switch to the system stack. */ + // sp = _tx_thread_system_stack_ptr; + + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x4, [x3, x1, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + RET // Return to caller + + // } + // else + // { + +__tx_thread_idle_system_save: + + /* Interrupt occurred in the scheduling loop. */ + + /* Not much to do here, just adjust the stack pointer, and return to IRQ + processing. */ + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the ISR enter function to indicate an ISR is executing. */ + + STP x29, x30, [sp, #-16]! // Save x29, x30 + BL _tx_execution_isr_enter // Call the ISR enter function + LDP x29, x30, [sp], #16 // Recover x29, x30 +#endif + + ADD sp, sp, #48 // Recover saved registers + RET // Continue IRQ processing + + // } +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_disable.c b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_disable.c new file mode 100644 index 00000000..3e5d7e21 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_disable.c @@ -0,0 +1,97 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function disables the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_disable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now set the FP enable flag to false in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_FALSE; + } + } +} + diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_enable.c b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_enable.c new file mode 100644 index 00000000..4e69205c --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_fp_enable.c @@ -0,0 +1,96 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define TX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "tx_thread.h" + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_fp_enable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enabled the FP for the currently executing thread. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +VOID _tx_thread_fp_enable(VOID) +{ + +TX_THREAD *thread_ptr; +ULONG system_state; + + + /* Pickup the current thread pointer. */ + TX_THREAD_GET_CURRENT(thread_ptr); + + /* Get the system state. */ + system_state = TX_THREAD_GET_SYSTEM_STATE(); + + /* Make sure it is not NULL. */ + if (thread_ptr != TX_NULL) + { + + /* Thread is running... make sure the call is from the thread context. */ + if (system_state == 0) + { + + /* Yes, now setup the FP enable flag in the TX_THREAD structure. */ + thread_ptr -> tx_thread_fp_enable = TX_TRUE; + } + } +} + diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_control.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000..6a5a7741 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .type _tx_thread_interrupt_control, @function +_tx_thread_interrupt_control: + + /* Pickup current interrupt lockout posture. */ + + MRS x1, DAIF // Pickup current interrupt posture + + /* Apply the new interrupt posture. */ + + MSR DAIF, x0 // Set new interrupt posture + MOV x0, x1 // Setup return value + RET // Return to caller +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_disable.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000..d0062ef8 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable ARMv8-A */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(void) +// { + .global _tx_thread_interrupt_disable + .type _tx_thread_interrupt_disable, @function +_tx_thread_interrupt_disable: + + /* Pickup current interrupt lockout posture. */ + + MRS x0, DAIF // Pickup current interrupt lockout posture + + /* Mask interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + RET // Return to caller +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_restore.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000..d4507819 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring interrupts to the state */ +/* returned by a previous _tx_thread_interrupt_disable call. */ +/* */ +/* INPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_restore(UINT old_posture) +// { + .global _tx_thread_interrupt_restore + .type _tx_thread_interrupt_restore, @function +_tx_thread_interrupt_restore: + + /* Restore the old interrupt posture. */ + + MSR DAIF, x0 // Setup the old posture + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_schedule.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000..35a8fc96 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_schedule.S @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* _tx_thread_context_restore Restore thread's context */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .type _tx_thread_schedule, @function +_tx_thread_schedule: + + /* Enable interrupts. */ + + MSR DAIFClr, 0x3 // Enable interrupts + + /* Pickup the CPU ID. */ + + MRS x20, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #16, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x20, #8, #8 // Isolate cluster ID +#endif + UBFX x20, x20, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x20, x20, x1, LSL #2 // Calculate CPU ID +#endif + + /* Wait for a thread to execute. */ + // do + // { + + LDR x1, =_tx_thread_execute_ptr // Address of thread execute ptr + +#ifdef TX_ENABLE_WFI +__tx_thread_schedule_loop: + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BNE _tx_thread_schedule_thread // + MSR DAIFClr, 0x3 // Enable interrupts + WFI // + B __tx_thread_schedule_loop // Keep looking for a thread +_tx_thread_schedule_thread: +#else + MSR DAIFSet, 0x3 // Lockout interrupts + LDR x0, [x1, x20, LSL #3] // Pickup next thread to execute + CMP x0, #0 // Is it NULL? + BEQ _tx_thread_schedule // Keep looking for a thread +#endif + + // } + // while(_tx_thread_execute_ptr == TX_NULL); + + /* Get the lock for accessing the thread's ready bit. */ + + MOV w2, #280 // Build offset to the lock + ADD x2, x0, x2 // Get the address to the lock + LDAXR w3, [x2] // Pickup the lock value + CMP w3, #0 // Check if it's available + BNE _tx_thread_schedule // No, lock not available + MOV w3, #1 // Build the lock set value + STXR w4, w3, [x2] // Try to get the lock + CMP w4, #0 // Check if we got the lock + BNE _tx_thread_schedule // No, another core got it first + DMB ISH // Ensure write to lock completes + + /* Now make sure the thread's ready bit is set. */ + + LDR w3, [x0, #260] // Pickup the thread ready bit + CMP w3, #0 // Is it set? + BNE _tx_thread_ready_for_execution // Yes, schedule the thread + + /* The ready bit isn't set. Release the lock and jump back to the scheduler. */ + + MOV w3, #0 // Build clear value + STR w3, [x2] // Release the lock + DMB ISH // Ensure write to lock completes + B _tx_thread_schedule // Jump back to the scheduler + +_tx_thread_ready_for_execution: + + /* We have a thread to execute. */ + + /* Clear the ready bit and release the lock. */ + + MOV w3, #0 // Build clear value + STR w3, [x0, #260] // Store it back in the thread control block + DMB ISH + MOV w3, #0 // Build clear value for the lock + STR w3, [x2] // Release the lock + DMB ISH + + /* Setup the current thread pointer. */ + // _tx_thread_current_ptr = _tx_thread_execute_ptr; + + LDR x2, =_tx_thread_current_ptr // Pickup address of current thread + STR x0, [x2, x20, LSL #3] // Setup current thread pointer + + LDR x1, [x1, x20, LSL #3] // Reload the execute pointer + CMP w0, w1 // Did it change? + BEQ _execute_pointer_did_not_change // If not, skip handling + + /* In the time between reading the execute pointer and assigning + it to the current pointer, the execute pointer was changed by + some external code. If the current pointer was still null when + the external code checked if a core preempt was necessary, then + it wouldn't have done it and a preemption will be missed. To + handle this, undo some things and jump back to the scheduler so + it can schedule the new thread. */ + + MOV w1, #0 // Build clear value + STR x1, [x2, x20, LSL #3] // Clear current thread pointer + + MOV w1, #1 // Build set value + STR w1, [x0, #260] // Re-set the ready bit + DMB ISH // + + B _tx_thread_schedule // Jump back to the scheduler to schedule the new thread + +_execute_pointer_did_not_change: + /* Increment the run count for this thread. */ + // _tx_thread_current_ptr -> tx_thread_run_count++; + + LDR w2, [x0, #4] // Pickup run counter + LDR w3, [x0, #36] // Pickup time-slice for this thread + ADD w2, w2, #1 // Increment thread run-counter + STR w2, [x0, #4] // Store the new run counter + + /* Setup time-slice, if present. */ + // _tx_timer_time_slice = _tx_thread_current_ptr -> tx_thread_time_slice; + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + // variable + LDR x4, [x0, #8] // Switch stack pointers + MOV sp, x4 // + STR w3, [x2, x20, LSL #2] // Setup time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + + /* Call the thread entry function to indicate the thread is executing. */ + + MOV x19, x0 // Save x0 + BL _tx_execution_thread_enter // Call the thread execution enter function + MOV x0, x19 // Restore x0 +#endif + + /* Switch to the thread's stack. */ + // sp = _tx_thread_execute_ptr -> tx_thread_stack_ptr; + + /* Determine if an interrupt frame or a synchronous task suspension frame + is present. */ + + LDP x4, x5, [sp], #16 // Pickup saved SPSR/DAIF and ELR_EL1 + CMP x5, #0 // Check for synchronous context switch (ELR_EL1 = NULL) + BEQ _tx_solicited_return +#ifdef EL1 + MSR SPSR_EL1, x4 // Setup SPSR for return + MSR ELR_EL1, x5 // Setup point of interrupt +#else +#ifdef EL2 + MSR SPSR_EL2, x4 // Setup SPSR for return + MSR ELR_EL2, x5 // Setup point of interrupt +#else + MSR SPSR_EL3, x4 // Setup SPSR for return + MSR ELR_EL3, x5 // Setup point of interrupt +#endif +#endif +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_interrupt_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q30, q31, [sp], #32 // Recover q30, q31 + LDP q28, q29, [sp], #32 // Recover q28, q29 + LDP q26, q27, [sp], #32 // Recover q26, q27 + LDP q24, q25, [sp], #32 // Recover q24, q25 + LDP q22, q23, [sp], #32 // Recover q22, q23 + LDP q20, q21, [sp], #32 // Recover q20, q21 + LDP q18, q19, [sp], #32 // Recover q18, q19 + LDP q16, q17, [sp], #32 // Recover q16, q17 + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 + LDP q6, q7, [sp], #32 // Recover q6, q7 + LDP q4, q5, [sp], #32 // Recover q4, q5 + LDP q2, q3, [sp], #32 // Recover q2, q3 + LDP q0, q1, [sp], #32 // Recover q0, q1 +_skip_interrupt_fp_restore: +#endif + LDP x28, x29, [sp], #16 // Recover x28 + LDP x26, x27, [sp], #16 // Recover x26, x27 + LDP x24, x25, [sp], #16 // Recover x24, x25 + LDP x22, x23, [sp], #16 // Recover x22, x23 + LDP x20, x21, [sp], #16 // Recover x20, x21 + LDP x18, x19, [sp], #16 // Recover x18, x19 + LDP x16, x17, [sp], #16 // Recover x16, x17 + LDP x14, x15, [sp], #16 // Recover x14, x15 + LDP x12, x13, [sp], #16 // Recover x12, x13 + LDP x10, x11, [sp], #16 // Recover x10, x11 + LDP x8, x9, [sp], #16 // Recover x8, x9 + LDP x6, x7, [sp], #16 // Recover x6, x7 + LDP x4, x5, [sp], #16 // Recover x4, x5 + LDP x2, x3, [sp], #16 // Recover x2, x3 + LDP x0, x1, [sp], #16 // Recover x0, x1 + LDP x29, x30, [sp], #16 // Recover x29, x30 + ERET // Return to point of interrupt + +_tx_solicited_return: + +#ifdef ENABLE_ARM_FP + LDR w1, [x0, #268] // Pickup FP enable flag + CMP w1, #0 // Is FP enabled? + BEQ _skip_solicited_fp_restore // No, skip FP restore + LDP x0, x1, [sp], #16 // Pickup FPSR, FPCR + MSR FPSR, x0 // Recover FPSR + MSR FPCR, x1 // Recover FPCR + LDP q14, q15, [sp], #32 // Recover q14, q15 + LDP q12, q13, [sp], #32 // Recover q12, q13 + LDP q10, q11, [sp], #32 // Recover q10, q11 + LDP q8, q9, [sp], #32 // Recover q8, q9 +_skip_solicited_fp_restore: +#endif + LDP x27, x28, [sp], #16 // Recover x27, x28 + LDP x25, x26, [sp], #16 // Recover x25, x26 + LDP x23, x24, [sp], #16 // Recover x23, x24 + LDP x21, x22, [sp], #16 // Recover x21, x22 + LDP x19, x20, [sp], #16 // Recover x19, x20 + LDP x29, x30, [sp], #16 // Recover x29, x30 + MSR DAIF, x4 // Recover DAIF + RET // Return to caller +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_get.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_get.S new file mode 100644 index 00000000..1ba20773 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_get.S @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the currently running core number and returns it.*/ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Core ID */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_get + .type _tx_thread_smp_core_get, @function +_tx_thread_smp_core_get: + MRS x0, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #16, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x1, x0, #8, #8 // Isolate cluster ID +#endif + UBFX x0, x0, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x0, x0, x1, LSL #2 // Calculate CPU ID +#endif + RET + diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_preempt.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_preempt.S new file mode 100644 index 00000000..4feeca33 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_core_preempt.S @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_core_preempt Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function preempts the specified core in situations where the */ +/* thread corresponding to this core is no longer ready or when the */ +/* core must be used for a higher-priority thread. If the specified is */ +/* the current core, this processing is skipped since the will give up */ +/* control subsequently on its own. */ +/* */ +/* INPUT */ +/* */ +/* core The core to preempt */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_core_preempt + .type _tx_thread_smp_core_preempt, @function +_tx_thread_smp_core_preempt: + DSB ISH +#ifdef TX_ARMV8_2 + MOV x2, #0x1 // Build the target list field + LSL x3, x0, #16 // Build the affinity1 field + ORR x2, x2, x3 // Combine the fields +#else + MOV x2, #0x1 // + LSL x2, x2, x0 // Shift by the core ID +#endif + MSR ICC_SGI1R_EL1, x2 // Issue inter-core interrupt + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_state_get.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_state_get.S new file mode 100644 index 00000000..a6b4a5cb --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_state_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_state_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current state of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_state_get + .type _tx_thread_smp_current_state_get, @function +_tx_thread_smp_current_state_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_thread_get.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_thread_get.S new file mode 100644 index 00000000..b3f6f0af --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_current_thread_get.S @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_current_thread_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is gets the current thread of the calling core. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_current_thread_get + .type _tx_thread_smp_current_thread_get, @function +_tx_thread_smp_current_thread_get: + + MRS x1, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + LDR x3, =_tx_thread_current_ptr // Pickup the base of the current thread pointer array + LDR x0, [x3, x2, LSL #3] // Pickup the current thread pointer for this core + MSR DAIF, x1 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_initialize_wait.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_initialize_wait.S new file mode 100644 index 00000000..c806c02f --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_initialize_wait.S @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_initialize_wait Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the place where additional cores wait until */ +/* initialization is complete before they enter the thread scheduling */ +/* loop. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* Hardware */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_initialize_wait + .type _tx_thread_smp_initialize_wait, @function +_tx_thread_smp_initialize_wait: + + /* Lockout interrupts. */ + + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the Core ID. */ + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + + /* Make sure the system state for this core is TX_INITIALIZE_IN_PROGRESS before we check the release + flag. */ + + LDR w1, =0xF0F0F0F0 // Build TX_INITIALIZE_IN_PROGRESS flag + LDR x3, =_tx_thread_system_state // Pickup the base of the current system state array +wait_for_initialize: + LDR w0, [x3, x2, LSL #2] // Pickup the current system state for this core + CMP w0, w1 // Make sure the TX_INITIALIZE_IN_PROGRESS flag is set + BNE wait_for_initialize // Not equal, just spin here + + /* Save the system stack pointer for this core. */ + + LDR x0, =_tx_thread_system_stack_ptr // Pickup address of system stack ptr + MOV x1, sp // Pickup SP + SUB x1, x1, #15 // + BIC x1, x1, #0xF // Get 16-bit alignment + STR x1, [x0, x2, LSL #3] // Store system stack pointer + + + /* Pickup the release cores flag. */ + + LDR x4, =_tx_thread_smp_release_cores_flag // Build address of release cores flag +wait_for_release: + LDR w0, [x4, #0] // Pickup the flag + CMP w0, #0 // Is it set? + BEQ wait_for_release // Wait for the flag to be set + + /* Core 0 has released this core. */ + + /* Clear this core's system state variable. */ + + MOV x0, #0 // Build clear value + STR w0, [x3, x2, LSL #2] // Set the current system state for this core to zero + + /* Now wait for core 0 to finish it's initialization. */ + +core_0_wait_loop: + LDR w0, [x3, #0] // Pickup the current system state for core 0 + CMP w0, #0 // Is it 0? + BNE core_0_wait_loop // No, keep waiting for core 0 to finish its initialization + + /* Initialization is complete, enter the scheduling loop! */ + + B _tx_thread_schedule // Enter the scheduling loop for this core + + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_low_level_initialize.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_low_level_initialize.S new file mode 100644 index 00000000..ffb18050 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_low_level_initialize.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_low_level_initialize Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs low-level initialization of the booting */ +/* core. */ +/* */ +/* INPUT */ +/* */ +/* number_of_cores Number of cores */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_high_level ThreadX high-level init */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_low_level_initialize + .type _tx_thread_smp_low_level_initialize, @function +_tx_thread_smp_low_level_initialize: + + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protect.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protect.S new file mode 100644 index 00000000..9cde3e08 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protect.S @@ -0,0 +1,373 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include macros for modifying the wait list. */ +#include "tx_thread_smp_protection_wait_list_macros.h" + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_protect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets protection for running inside the ThreadX */ +/* source. This is acomplished by a combination of a test-and-set */ +/* flag and periodically disabling interrupts. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* improved SMP code, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_protect + .type _tx_thread_smp_protect, @function +_tx_thread_smp_protect: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR x2, =_tx_thread_smp_protection // Build address to protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it not this core? + BNE _protection_not_owned // No, the protection is not already owned + + /* We already have protection. */ + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + B _return + +_protection_not_owned: + + /* Is the lock available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Is the list empty? */ + // if (_tx_thread_smp_protect_wait_list_head == _tx_thread_smp_protect_wait_list_tail) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head + LDR w3, [x3] + LDR x4, =_tx_thread_smp_protect_wait_list_tail + LDR w4, [x4] + CMP w3, w4 + BNE _list_not_empty + + /* Try to get the lock. */ + // if (write_exclusive(&_tx_thread_smp_protection.tx_thread_smp_protect_in_force, 1) == SUCCESS) + // { + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + + /* We got the lock! */ + // _tx_thread_smp_protect_lock_got(); + + DMB ISH // Ensure write to protection finishes + _tx_thread_smp_protect_lock_got // Call the lock got function + + B _return + +_list_not_empty: + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _start_waiting + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _start_waiting // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _start_waiting // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _return + +_start_waiting: + + /* For one reason or another, we didn't get the lock. */ + + /* Increment wait count. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value + + /* Have we not added ourselves to the list yet? */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 1) + // { + + CMP w4, #1 + BNE _already_in_list0 // Is this core already waiting? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + // } + +_already_in_list0: + + /* Restore interrupts. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + + /* We do this until we have the lock. */ + // while (1) + // { + +_try_to_get_lock: + + /* Disable interrupts so we don't get preempted. */ + + MRS x0, DAIF // Pickup current interrupt posture + MSR DAIFSet, 0x3 // Lockout interrupts + + /* Pickup the CPU ID. */ + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x7, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x7, LSL #2 // Calculate CPU ID +#endif + + /* Do we already have protection? */ + // if (this_core == _tx_thread_smp_protection.tx_thread_smp_protect_core) + // { + + LDR w3, [x2, #4] // Pickup the owning core + CMP w3, w1 // Is it this core? + BEQ _got_lock_after_waiting // Yes, the protection is already owned. This means + // an ISR preempted us and got protection + + // } + + /* Are we at the front of the list? */ + // if (this_core == _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head]) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w3, [x3] // Get the value of the head + LDR x4, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w4, [x4, x3, LSL #2] // Get the value at the head index + + CMP w1, w4 + BNE _did_not_get_lock + + /* Is the lock still available? */ + // if (_tx_thread_smp_protection.tx_thread_smp_protect_in_force == 0) + // { + + LDAXR w3, [x2, #0] // Pickup the protection flag + CMP w3, #0 + BNE _did_not_get_lock // No, protection not available + + /* Get the lock. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_in_force = 1; + + MOV w3, #1 // Build lock value + STXR w4, w3, [x2, #0] // Attempt to get the protection + CMP w4, #0 + BNE _did_not_get_lock // Did it fail? + DMB ISH // + + /* Got the lock. */ + // _tx_thread_smp_protect_lock_got(); + + _tx_thread_smp_protect_lock_got + + /* Remove this core from the wait list. */ + // _tx_thread_smp_protect_remove_from_front_of_list(); + + _tx_thread_smp_protect_remove_from_front_of_list + + B _got_lock_after_waiting + +_did_not_get_lock: + + /* For one reason or another, we didn't get the lock. */ + + /* Were we removed from the list? This can happen if we're a thread + and we got preempted. */ + // if (_tx_thread_smp_protect_wait_counts[this_core] == 0) + // { + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + CMP w4, #0 + BNE _already_in_list1 // Is this core already in the list? + + /* Add ourselves to the list. */ + // _tx_thread_smp_protect_wait_list_add(this_core); + + _tx_thread_smp_protect_wait_list_add // Call macro to add ourselves to the list + + /* Our waiting count was also reset when we were preempted. Increment it again. */ + // _tx_thread_smp_protect_wait_counts[this_core]++; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w4, [x3, x1, LSL #2] // Load waiting value for this core + ADD w4, w4, #1 // Increment wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + // } + +_already_in_list1: + + /* Restore interrupts and try again. */ + + MSR DAIF, x0 // Restore interrupts + ISB // +#ifdef TX_ENABLE_WFE + WFE // Go into standby +#endif + B _try_to_get_lock // On waking, restart the protection attempt + +_got_lock_after_waiting: + + /* We're no longer waiting. */ + // _tx_thread_smp_protect_wait_counts[this_core]--; + + LDR x3, =_tx_thread_smp_protect_wait_counts // Load waiting list + LDR w4, [x3, x1, LSL #2] // Load current wait value + SUB w4, w4, #1 // Decrement wait value + STR w4, [x3, x1, LSL #2] // Store new wait value value + + /* Restore registers and return. */ + +_return: + + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h new file mode 100644 index 00000000..c56c8d38 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_protection_wait_list_macros.h @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .macro _tx_thread_smp_protect_lock_got + + /* Set the currently owned core. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_core = this_core; + + STR w1, [x2, #4] // Store this core + + /* Increment the protection count. */ + // _tx_thread_smp_protection.tx_thread_smp_protect_count++; + + LDR w3, [x2, #8] // Pickup ownership count + ADD w3, w3, #1 // Increment ownership count + STR w3, [x2, #8] // Store ownership count + DMB ISH + + .endm + + .macro _tx_thread_smp_protect_remove_from_front_of_list + + /* Remove ourselves from the list. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_head++] = 0xFFFFFFFF; + + MOV w3, #0xFFFFFFFF // Build the invalid core value + LDR x4, =_tx_thread_smp_protect_wait_list_head // Get the address of the head + LDR w5, [x4] // Get the value of the head + LDR x6, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w3, [x6, x5, LSL #2] // Store the invalid core value + ADD w5, w5, #1 // Increment the head + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_head == TX_THREAD_SMP_MAX_CORES + 1) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size // Load address of core list size + LDR w3, [x3] // Load the max cores value + CMP w5, w3 // Compare the head to it + BNE _store_new_head\@ // Are we at the max? + + // _tx_thread_smp_protect_wait_list_head = 0; + + EOR w5, w5, w5 // We're at the max. Set it to zero + + // } + +_store_new_head\@: + + STR w5, [x4] // Store the new head + + /* We have the lock! */ + DMB ISH // Ensure write to protection finishes + + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_lock_get +// VOID _tx_thread_smp_protect_wait_list_lock_get() +// { + /* We do this until we have the lock. */ + // while (1) + // { + +_tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@: + + // Is the list lock available? */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = load_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force); + + LDR x1, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + LDAXR w2, [x1] // Pickup the protection flag + + // if (protect_in_force == 0) + // { + + CMP w2, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // No, protection not available + + /* Try to get the list. */ + // int status = store_exclusive(&_tx_thread_smp_protect_wait_list_lock_protect_in_force, 1); + + MOV w2, #1 // Build lock value + STXR w3, w2, [x1] // Attempt to get the protection + + /* if (status == SUCCESS) */ + + CMP w3, #0 + BNE _tx_thread_smp_protect_wait_list_lock_get__try_to_get_lock\@ // Did it fail? If so, try again. + + /* We have the lock! */ + // return; + + .endm + + + .macro _tx_thread_smp_protect_wait_list_add +// VOID _tx_thread_smp_protect_wait_list_add(UINT new_core) +// { + + /* We're about to modify the list, so get the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + STP x1, x2, [sp, #-16]! // Save registers we'll be using + + _tx_thread_smp_protect_wait_list_lock_get + + LDP x1, x2, [sp], #16 + + /* Add this core. */ + // _tx_thread_smp_protect_wait_list[_tx_thread_smp_protect_wait_list_tail++] = new_core; + + LDR x3, =_tx_thread_smp_protect_wait_list_tail // Get the address of the tail + LDR w4, [x3] // Get the value of tail + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + STR w1, [x5, x4, LSL #2] // Store the new core value + ADD w4, w4, #1 // Increment the tail + + /* Did we wrap? */ + // if (_tx_thread_smp_protect_wait_list_tail == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_size // Load max cores address + LDR w5, [x5] // Load max cores value + CMP w4, w5 // Compare max cores to tail + BNE _tx_thread_smp_protect_wait_list_add__no_wrap\@ // Did we wrap? + + // _tx_thread_smp_protect_wait_list_tail = 0; + + MOV w4, #0 + + // } + +_tx_thread_smp_protect_wait_list_add__no_wrap\@: + + STR w4, [x3] // Store the new tail value. + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w3, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force + STR w3, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + .endm + + + .macro _tx_thread_smp_protect_wait_list_remove +// VOID _tx_thread_smp_protect_wait_list_remove(UINT core) +// { + + /* Get the core index. */ + // UINT core_index; + // for (core_index = 0;; core_index++) + + EOR w4, w4, w4 // Clear for 'core_index' + LDR x2, =_tx_thread_smp_protect_wait_list // Get the address of the list + + // { + +_tx_thread_smp_protect_wait_list_remove__check_cur_core\@: + + /* Is this the core? */ + // if (_tx_thread_smp_protect_wait_list[core_index] == core) + // { + // break; + + LDR w3, [x2, x4, LSL #2] // Get the value at the current index + CMP w3, w8 // Did we find the core? + BEQ _tx_thread_smp_protect_wait_list_remove__found_core\@ + + // } + + ADD w4, w4, #1 // Increment cur index + B _tx_thread_smp_protect_wait_list_remove__check_cur_core\@ // Restart the loop + + // } + +_tx_thread_smp_protect_wait_list_remove__found_core\@: + + /* We're about to modify the list. Get the lock. We need the lock because another + core could be simultaneously adding (a core is simultaneously trying to get + the inter-core lock) or removing (a core is simultaneously being preempted, + like what is currently happening). */ + // _tx_thread_smp_protect_wait_list_lock_get(); + + MOV x6, x1 + _tx_thread_smp_protect_wait_list_lock_get + MOV x1, x6 + + /* We remove by shifting. */ + // while (core_index != _tx_thread_smp_protect_wait_list_tail) + // { + +_tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@: + + LDR x2, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w2, [x2] // Load tail value + CMP w4, w2 // Compare cur index and tail + BEQ _tx_thread_smp_protect_wait_list_remove__removed\@ + + // UINT next_index = core_index + 1; + + MOV w2, w4 // Move current index to next index register + ADD w2, w2, #1 // Add 1 + + // if (next_index == _tx_thread_smp_protect_wait_list_size) + // { + + LDR x3, =_tx_thread_smp_protect_wait_list_size + LDR w3, [x3] + CMP w2, w3 + BNE _tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@ + + // next_index = 0; + + MOV w2, #0 + + // } + +_tx_thread_smp_protect_wait_list_remove__next_index_no_wrap\@: + + // list_cores[core_index] = list_cores[next_index]; + + LDR x5, =_tx_thread_smp_protect_wait_list // Get the address of the list + LDR w3, [x5, x2, LSL #2] // Get the value at the next index + STR w3, [x5, x4, LSL #2] // Store the value at the current index + + // core_index = next_index; + + MOV w4, w2 + + B _tx_thread_smp_protect_wait_list_remove__compare_index_to_tail\@ + + // } + +_tx_thread_smp_protect_wait_list_remove__removed\@: + + /* Now update the tail. */ + // if (_tx_thread_smp_protect_wait_list_tail == 0) + // { + + LDR x5, =_tx_thread_smp_protect_wait_list_tail // Load tail address + LDR w4, [x5] // Load tail value + CMP w4, #0 + BNE _tx_thread_smp_protect_wait_list_remove__tail_not_zero\@ + + // _tx_thread_smp_protect_wait_list_tail = _tx_thread_smp_protect_wait_list_size; + + LDR x2, =_tx_thread_smp_protect_wait_list_size + LDR w4, [x2] + + // } + +_tx_thread_smp_protect_wait_list_remove__tail_not_zero\@: + + // _tx_thread_smp_protect_wait_list_tail--; + + SUB w4, w4, #1 + STR w4, [x5] // Store new tail value + DMB ISH // Ensure that accesses to shared resource have completed + + /* Release the list lock. */ + // _tx_thread_smp_protect_wait_list_lock_protect_in_force = 0; + + MOV w2, #0 // Build lock value + LDR x4, =_tx_thread_smp_protect_wait_list_lock_protect_in_force // Load lock address + STR w2, [x4] // Store the new value + DMB ISH // Ensure write to protection finishes + + /* We're no longer waiting. Note that this should be zero since, again, + this function is only called when a thread preemption is occurring. */ + // _tx_thread_smp_protect_wait_counts[core]--; + LDR x4, =_tx_thread_smp_protect_wait_counts // Load wait list counts + LDR w2, [x4, x8, LSL #2] // Load waiting value + SUB w2, w2, #1 // Subtract 1 + STR w2, [x4, x8, LSL #2] // Store new waiting value + + .endm + diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_time_get.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_time_get.S new file mode 100644 index 00000000..510ac91d --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_time_get.S @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_time_get Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function gets the global time value that is used for debug */ +/* information and event tracing. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* 32-bit time stamp */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_time_get + .type _tx_thread_smp_time_get, @function +_tx_thread_smp_time_get: + MOV x0, #0 // FIXME: Get timer + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_unprotect.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_unprotect.S new file mode 100644 index 00000000..a783cde6 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_smp_unprotect.S @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread - Low Level SMP Support */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_smp_unprotect Cortex-A35-SMP/AC6 */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases previously obtained protection. The supplied */ +/* previous SR is restored. If the value of _tx_thread_system_state */ +/* and _tx_thread_preempt_disable are both zero, then multithreading */ +/* is enabled as well. */ +/* */ +/* INPUT */ +/* */ +/* Previous Status Register */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX Source */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ + .global _tx_thread_smp_unprotect + .type _tx_thread_smp_unprotect, @function +_tx_thread_smp_unprotect: + MSR DAIFSet, 0x3 // Lockout interrupts + + MRS x1, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #16, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x2, x1, #8, #8 // Isolate cluster ID +#endif + UBFX x1, x1, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x1, x1, x2, LSL #2 // Calculate CPU ID +#endif + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + LDR w3, [x2, #4] // Pickup the owning core + CMP w1, w3 // Is it this core? + BNE _still_protected // If this is not the owning core, protection is in force elsewhere + + LDR w3, [x2, #8] // Pickup the protection count + CMP w3, #0 // Check to see if the protection is still active + BEQ _still_protected // If the protection count is zero, protection has already been cleared + + SUB w3, w3, #1 // Decrement the protection count + STR w3, [x2, #8] // Store the new count back + CMP w3, #0 // Check to see if the protection is still active + BNE _still_protected // If the protection count is non-zero, protection is still in force + LDR x2,=_tx_thread_preempt_disable // Build address of preempt disable flag + LDR w3, [x2] // Pickup preempt disable flag + CMP w3, #0 // Is the preempt disable flag set? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protect_wait_counts // Build build address of wait counts + LDR w3, [x2, x1, LSL #2] // Pickup wait list value + CMP w3, #0 // Are any entities on this core waiting? + BNE _still_protected // Yes, skip the protection release + + LDR x2,=_tx_thread_smp_protection // Build address of protection structure + MOV w3, #0xFFFFFFFF // Build invalid value + STR w3, [x2, #4] // Mark the protected core as invalid + DMB ISH // Ensure that accesses to shared resource have completed + MOV w3, #0 // Build release protection value + STR w3, [x2, #0] // Release the protection + DSB ISH // To ensure update of the protection occurs before other CPUs awake + +_still_protected: +#ifdef TX_ENABLE_WFE + SEV // Send event to other CPUs, wakes anyone waiting on the protection (using WFE) +#endif + MSR DAIF, x0 // Restore interrupt posture + RET diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_stack_build.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000..73e79e79 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread */ +/* function_ptr Pointer to entry function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .type _tx_thread_stack_build, @function +_tx_thread_stack_build: + + + /* Build an interrupt frame. On Cortex-A35 it should look like this: + + Stack Top: SSPR Initial SSPR + ELR Point of interrupt + x28 Initial value for x28 + not used Not used + x26 Initial value for x26 + x27 Initial value for x27 + x24 Initial value for x24 + x25 Initial value for x25 + x22 Initial value for x22 + x23 Initial value for x23 + x20 Initial value for x20 + x21 Initial value for x21 + x18 Initial value for x18 + x19 Initial value for x19 + x16 Initial value for x16 + x17 Initial value for x17 + x14 Initial value for x14 + x15 Initial value for x15 + x12 Initial value for x12 + x13 Initial value for x13 + x10 Initial value for x10 + x11 Initial value for x11 + x8 Initial value for x8 + x9 Initial value for x9 + x6 Initial value for x6 + x7 Initial value for x7 + x4 Initial value for x4 + x5 Initial value for x5 + x2 Initial value for x2 + x3 Initial value for x3 + x0 Initial value for x0 + x1 Initial value for x1 + x29 Initial value for x29 (frame pointer) + x30 Initial value for x30 (link register) + 0 For stack backtracing + + Stack Bottom: (higher memory address) */ + + LDR x4, [x0, #24] // Pickup end of stack area + BIC x4, x4, #0xF // Ensure 16-byte alignment + + /* Actually build the stack frame. */ + + MOV x2, #0 // Build clear value + MOV x3, #0 // + + STP x2, x3, [x4, #-16]! // Set backtrace to 0 + STP x2, x3, [x4, #-16]! // Set initial x29, x30 + STP x2, x3, [x4, #-16]! // Set initial x0, x1 + STP x2, x3, [x4, #-16]! // Set initial x2, x3 + STP x2, x3, [x4, #-16]! // Set initial x4, x5 + STP x2, x3, [x4, #-16]! // Set initial x6, x7 + STP x2, x3, [x4, #-16]! // Set initial x8, x9 + STP x2, x3, [x4, #-16]! // Set initial x10, x11 + STP x2, x3, [x4, #-16]! // Set initial x12, x13 + STP x2, x3, [x4, #-16]! // Set initial x14, x15 + STP x2, x3, [x4, #-16]! // Set initial x16, x17 + STP x2, x3, [x4, #-16]! // Set initial x18, x19 + STP x2, x3, [x4, #-16]! // Set initial x20, x21 + STP x2, x3, [x4, #-16]! // Set initial x22, x23 + STP x2, x3, [x4, #-16]! // Set initial x24, x25 + STP x2, x3, [x4, #-16]! // Set initial x26, x27 + STP x2, x3, [x4, #-16]! // Set initial x28 +#ifdef EL1 + MOV x2, #0x4 // Build initial SPSR (EL1) +#else +#ifdef EL2 + MOV x2, #0x8 // Build initial SPSR (EL2) +#else + MOV x2, #0xC // Build initial SPSR (EL3) +#endif +#endif + MOV x3, x1 // Build initial ELR + STP x2, x3, [x4, #-16]! // Set initial SPSR & ELR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = x2; + + STR x4, [x0, #8] // Save stack pointer in thread's + MOV x3, #1 // Build ready flag + STR w3, [x0, #260] // Set ready flag + RET // Return to caller + +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_thread_system_return.S b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000..68c92a33 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_thread_system_return.S @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* 01-31-2022 Andres Mlinar Updated comments, */ +/* added ARMv8.2-A support, */ +/* resulting in version 6.1.10 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .global _tx_thread_system_return + .type _tx_thread_system_return, @function +_tx_thread_system_return: + + /* Save minimal context on the stack. */ + + MRS x0, DAIF // Pickup DAIF + MSR DAIFSet, 0x3 // Lockout interrupts + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + STP x19, x20, [sp, #-16]! // Save x19, x20 + STP x21, x22, [sp, #-16]! // Save x21, x22 + STP x23, x24, [sp, #-16]! // Save x23, x24 + STP x25, x26, [sp, #-16]! // Save x25, x26 + STP x27, x28, [sp, #-16]! // Save x27, x28 + MRS x8, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #16, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x8, #8, #8 // Isolate cluster ID +#endif + UBFX x8, x8, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x8, x8, x3, LSL #2 // Calculate CPU ID +#endif + LDR x5, =_tx_thread_current_ptr // Pickup address of current ptr + LDR x6, [x5, x8, LSL #3] // Pickup current thread pointer + +#ifdef ENABLE_ARM_FP + LDR w7, [x6, #268] // Pickup FP enable flag + CMP w7, #0 // Is FP enabled? + BEQ _skip_fp_save // No, skip FP save + STP q8, q9, [sp, #-32]! // Save q8, q9 + STP q10, q11, [sp, #-32]! // Save q10, q11 + STP q12, q13, [sp, #-32]! // Save q12, q13 + STP q14, q15, [sp, #-32]! // Save q14, q15 + MRS x2, FPSR // Pickup FPSR + MRS x3, FPCR // Pickup FPCR + STP x2, x3, [sp, #-16]! // Save FPSR, FPCR +_skip_fp_save: +#endif + + MOV x1, #0 // Clear x1 + STP x0, x1, [sp, #-16]! // Save DAIF and clear value for ELR_EK1 + +#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. */ + + MOV x19, x5 // Save x5 + MOV x20, x6 // Save x6 + MOV x21, x8 // Save x2 + BL _tx_execution_thread_exit // Call the thread exit function + MOV x8, x21 // Restore x2 + MOV x5, x19 // Restore x5 + MOV x6, x20 // Restore x6 +#endif + + LDR x2, =_tx_timer_time_slice // Pickup address of time slice + LDR w1, [x2, x8, LSL #2] // Pickup current time slice + + /* Save current stack and switch to system stack. */ + // _tx_thread_current_ptr[core] -> tx_thread_stack_ptr = sp; + // sp = _tx_thread_system_stack_ptr[core]; + + MOV x4, sp // + STR x4, [x6, #8] // Save thread stack pointer + LDR x3, =_tx_thread_system_stack_ptr // Pickup address of system stack + LDR x4, [x3, x8, LSL #3] // Pickup system stack pointer + MOV sp, x4 // Setup system stack pointer + + /* Determine if the time-slice is active. */ + // if (_tx_timer_time_slice[core]) + // { + + MOV x4, #0 // Build clear value + CMP w1, #0 // Is a time-slice active? + BEQ __tx_thread_dont_save_ts // No, don't save the time-slice + + /* Save the current remaining time-slice. */ + // _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice; + // _tx_timer_time_slice = 0; + + STR w4, [x2, x8, LSL #2] // Clear time-slice + STR w1, [x6, #36] // Store current time-slice + + // } +__tx_thread_dont_save_ts: + + /* Clear the current thread pointer. */ + // _tx_thread_current_ptr = TX_NULL; + + STR x4, [x5, x8, LSL #3] // Clear current thread pointer + + /* Set ready bit in thread control block. */ + + MOV x3, #1 // Build ready value + STR w3, [x6, #260] // Make the thread ready + DMB ISH // + + /* Now clear protection. It is assumed that protection is in force whenever this routine is called. */ + + LDR x3, =_tx_thread_smp_protection // Pickup address of protection structure + LDR x1, =_tx_thread_preempt_disable // Build address to preempt disable flag + STR w4, [x1, #0] // Clear preempt disable flag + STR w4, [x3, #8] // Cear protection count + MOV x1, #0xFFFFFFFF // Build invalid value + STR w1, [x3, #4] // Set core to an invalid value + DMB ISH // Ensure that accesses to shared resource have completed + STR w4, [x3, #0] // Clear protection + DSB ISH // To ensure update of the shared resource occurs before other CPUs awake + SEV // Send event to other CPUs, wakes anyone waiting on a mutex (using WFE) + B _tx_thread_schedule // Jump to scheduler! + +// } diff --git a/ports_smp/cortex_a78_smp/gnu/src/tx_timer_interrupt.S b/ports_smp/cortex_a78_smp/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000..b73825b4 --- /dev/null +++ b/ports_smp/cortex_a78_smp/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + + .text + .align 3 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt ARMv8-A-SMP */ +/* 6.1.10 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* interrupt context save/restore functions are called along with the */ +/* expiration functions. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { + .global _tx_timer_interrupt + .type _tx_timer_interrupt, @function +_tx_timer_interrupt: + + MRS x2, MPIDR_EL1 // Pickup the core ID +#ifdef TX_ARMV8_2 +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #16, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #8, #8 // Isolate core ID +#else +#if TX_THREAD_SMP_CLUSTERS > 1 + UBFX x3, x2, #8, #8 // Isolate cluster ID +#endif + UBFX x2, x2, #0, #8 // Isolate core ID +#endif +#if TX_THREAD_SMP_CLUSTERS > 1 + ADDS x2, x2, x3, LSL #2 // Calculate CPU ID +#endif + CMP x2, #0 // Is this core 0? + BEQ __tx_process_timer // If desired core, continue processing + RET // Simply return if different core +__tx_process_timer: + + /* Upon entry to this routine, it is assumed that context save has already + been called, and therefore the compiler scratch registers are available + for use. */ + + STP x27, x28, [sp, #-16]! // Save x27, x28 + STP x29, x30, [sp, #-16]! // Save x29 (frame pointer), x30 (link register) + + /* Get inter-core protection. */ + + BL _tx_thread_smp_protect // Get inter-core protection + MOV x28, x0 // Save the return value in preserved register + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR x1, =_tx_timer_system_clock // Pickup address of system clock + LDR w0, [x1, #0] // Pickup system clock + ADD w0, w0, #1 // Increment system clock + STR w0, [x1, #0] // Store new system clock + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR x1, =_tx_timer_current_ptr // Pickup current timer pointer addr + LDR x0, [x1, #0] // Pickup current timer + LDR x2, [x0, #0] // Pickup timer list entry + CMP x2, #0 // Is there anything in the list? + BEQ __tx_timer_no_timer // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR x3, =_tx_timer_expired // Pickup expiration flag address + MOV w2, #1 // Build expired value + STR w2, [x3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD x0, x0, #8 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR x3, =_tx_timer_list_end // Pickup addr of timer list end + LDR x2, [x3, #0] // Pickup list end + CMP x0, x2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR x3, =_tx_timer_list_start // Pickup addr of timer list start + LDR x0, [x3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR x0, [x1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR x1, =_tx_timer_expired // Pickup addr of expired flag + LDR w0, [x1, #0] // Pickup timer expired flag + CMP w0, #0 // Check for timer expiration + BEQ __tx_timer_dont_activate // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Call time-slice processing. */ + // _tx_thread_time_slice(); + BL _tx_thread_time_slice // Call time-slice processing + + /* Release inter-core protection. */ + + MOV x0, x28 // Pass the previous status register back + BL _tx_thread_smp_unprotect // Release protection + + LDP x29, x30, [sp], #16 // Recover x29, x30 + LDP x27, x28, [sp], #16 // Recover x27, x28 + RET // Return to caller + +// } diff --git a/utility/rtos_compatibility_layers/FreeRTOS/tx_freertos.c b/utility/rtos_compatibility_layers/FreeRTOS/tx_freertos.c index 5f7955f5..ee617a59 100644 --- a/utility/rtos_compatibility_layers/FreeRTOS/tx_freertos.c +++ b/utility/rtos_compatibility_layers/FreeRTOS/tx_freertos.c @@ -26,6 +26,9 @@ /* 10-15-2021 William E. Lamie Modified comment(s), and */ /* fixed compiler warnings, */ /* resulting in version 6.1.7 */ +/* 01-31-2022 William E. Lamie Modified comment(s), and */ +/* fixed compiler warnings, */ +/* resulting in version 6.1.10 */ /* */ /**************************************************************************/ @@ -1334,43 +1337,30 @@ BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore) configASSERT(xSemaphore != NULL); - - if(xSemaphore->p_set != NULL) { - TX_DISABLE; - _tx_thread_preempt_disable++; - } - if(xSemaphore->is_mutex == 1u) { ret = tx_mutex_put(&xSemaphore->mutex); if(ret != TX_SUCCESS) { return pdFALSE; } - } else { - if(xSemaphore->p_set == NULL) { - TX_DISABLE; - _tx_thread_preempt_disable++; - } - if(xSemaphore->sem.tx_semaphore_count >= xSemaphore->max_count) { - /* Maximum semaphore count reached return failure. */ - _tx_thread_preempt_disable--; - TX_RESTORE - return pdFALSE; - } + return pdTRUE; + } - ret = tx_semaphore_put(&xSemaphore->sem); - if(ret != TX_SUCCESS) { - _tx_thread_preempt_disable--; - TX_RESTORE; - return pdFALSE; - } + TX_DISABLE; + _tx_thread_preempt_disable++; - if(xSemaphore->p_set == NULL) { - _tx_thread_preempt_disable--; - TX_RESTORE; + if(xSemaphore->sem.tx_semaphore_count >= xSemaphore->max_count) { + /* Maximum semaphore count reached return failure. */ + _tx_thread_preempt_disable--; + TX_RESTORE + return pdFALSE; + } - _tx_thread_system_preempt_check(); - } + ret = tx_semaphore_put(&xSemaphore->sem); + if(ret != TX_SUCCESS) { + _tx_thread_preempt_disable--; + TX_RESTORE; + return pdFALSE; } if(xSemaphore->p_set != NULL) { @@ -1383,13 +1373,13 @@ BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore) TX_FREERTOS_ASSERT_FAIL(); return pdFALSE; } - - _tx_thread_preempt_disable--; - TX_RESTORE; - - _tx_thread_system_preempt_check(); } + _tx_thread_preempt_disable--; + TX_RESTORE; + + _tx_thread_system_preempt_check(); + return pdTRUE; }

-?!@2 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 diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.language.settings.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.language.settings.xml deleted file mode 100644 index d88b11c6..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/sample_threadx.language.settings.xml +++ /dev/null @@ -1,4787 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/shareddefaults.xml b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/shareddefaults.xml deleted file mode 100644 index c4b91cfa..00000000 --- a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/shareddefaults.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.1443481396650.pdom b/ports_smp/arc_hs_smp/metaware/example_build/.metadata/.plugins/org.eclipse.cdt.core/tx.1443481396650.pdom deleted file mode 100644 index 3f46ccd32b064289b8938a82e873fa882a2513f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 806912 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�ISdISlt5wf5Z#-y_oOck8CP5J=w~n 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܌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 z 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

9J?!Nbi%O@J}jw4MISTtOpL@PY&Tv&IiI4bEbsC zS;O)WbEYc#d7r6yD{L`HxQY>4JI52*PcMIhWZ^y#L#Q z4C_S*=P_LigX6x_jGOX_>uu_d`-F0j4)zJsoA_uX6}*Mt@yMM8#=TUz$2yCRJFVR1 z&NAb6mHQ%Rg>kcO7>}=6ZQPD>k5g;G=tX%y9{j$^glqZ}gMEZt8^c|qeI|Sr%<=fz zhm4ytV?6k6k8x8*j|abf*SL46@Jn#ef`*04{YmG~jJrd*Cp#}0H+g&yB6j+ zl;6n$_f&`ZLO6cV+DR!wR%c%lVRW{y zxu>&j4VNuxj}`5A6u6ZG-`|L*I;qc8p}C80^l}f-oQF{$<7OFFgRz)rG|P>@dLQ^L zbT80%J<)v8;2kq~@4$>+W8wpTukk;KGG}~MbYF|d__M65=R3}G#$=f>ypkr?Jk}W# zQ902owSV;U`FU`U{&3Hti6$v&+)OXmN3AmM4w~iIi)GyWPR_HFC;5JwCPe&x<32zW zBK?SQAEgNqe#W@priqV9;Jf%f>j>9t%`)x=nwMamm~nFsC;CZryN$bt<`ndHje8eO zh@Ab#eSl^y_FNjbPxCX_b7|a86zg73}Qkx|p5K$^MAGN)my@! z6>}VS5WGKxpPfD?j`_;;u_rC*djfVl-seD%{RIJucON`#dZiXUh}nSNLD+42=@R1s zgCO+S9@z8}pa*fN(#M{-r0-SO3+RC%D2oMz-e17Orq^Jlk3DyxcM^7+-g7AYr9#Z$ z(fKPpHeNY0kNqG4iT9WAu;Wc4Ug2l<@Fm{=1G`P{6kb{-#0(ytV}WGjEiBXP^aWMo z9StNq-X`mNIldwB{#J;d3pI@WGXbG@afseh&~t%AexYU{^ga`!S6i;zT%afrdY6Uh zZM5`LOeal05Dd4pV z5=_BM89Y=+1t)+xL zJf0OUAb39lp3N_P$UhH$EMICe#A6!C;}8KUSQXTvGF~nYmH0{Olr^4`2YW;Vf*QI3_e5oPgE`v^| z!G%G6nmUC5d5e5YV75@7{;YE8ABH=t4V#EY*lav_%uI9ZS{*7_1;@!~y$E%kWl?SF zJoO2>qmap{K5CsVyPIXmhYDuKizlzsPzq9WR-w$4)fBMQ9K)jTtPk} z4j;O&#Y1@@9JB;>zAGT~=D@?IHw*O07o^8`h2G7ui+q9A>B5u5BOvr{gojP95%gU6 zksi|}^jcxJWoQQOVr1wU;4vZPF!+!j(;JqdXMpoL_zOtd!!ooL>FX3En1W|y==%1r zt78a;-zVYCYH$?{zfYn8aRLGvT8`W)g^a{pahB&CLN>lh1b&|_3+WbM9h*~!pAjVM zSO$5}dm>mi8iYf%i?YuK?)76InZR$OESsWS`iFEHLT+cZDjtzsvg2U=@)aG}GKZ4G z7?K{%+K~67Tx(yQ!d|-OROdRC$CrRPNuOgQjGKE2M0R~JS#@r-7fwQrth=z7UjkCX zWJ_BHa6CuqC~^2u7F>@<>Km+T?AE2lfYAFQ+%~-zEqb<$x)pe~Oj!kYF*4;5;2pz* zCQ3bwSJG?uw^kwEMuZ7S+QTyC6$JdgAi)$oOQsZcp8D0Y^{R;V{-q(A!uw^oW(F)> z7Wc+sz0m}&1;Dujhp~ollOIov!#byN{jtR132|r6c&-J2eW28t(@#6*($zXpyg#H2 zVqT0i8N_xb55hs(XRte@pb9A9w@~Idy6Ude8*m4gA4*1`AM=4^ggK0DFu8QW2DtDo z)JMs8B4ua6ZiDdTWO&jpd_EK&`;n}(!{M>+hvQ}53P_18gomVoVH@$dv=|V)1#sJS ze+OP--DFxgY(hNRZ^2{Neb(t>>i$vSrSUKhKBPBP-LFBIfTTTK_xB*6U65c3p45FD z`MAEAbB$)ql+8ZF_a}}pBdq?!M6_a+qH`s1PKnXWsC!W#BXXSm4ZE)acf>{iBh&}E zzHRl|b@#Z6T{$hY#^tG%?dv<+SFU$IwVp!8p>C(%MLdbbKCFlDQ|rD|e91pQE&Ul= z4|EV$rNY%~8N;}w8nIoe$8su9ksb^Iro>bsnI7hh%MAP zHQh+{R?wGKE+DGF6ed)Sh41PJV7t9NwXS_l=j|p+ogSXKKz@m0!6i%c5;({YbThq| zTx$BiaI@U2U7eT2W4T4qSTZl@PdEMf?R>wECc^n%6ysOKi$X3bf4G?k>{rh=;Ypfc zfrZB1L=%ZvW!xP!IVk;s#tI}qtvRiNytD=oDA|yUGTRhn;dGq2Q9pf_MIi{X{B(wE zj|(yU@S(NaEFZR2A{A!A?&1*;>B914JDD^AjR-)ofxmUzNj&n_JUlkNC2$i@Kg$JuFznk<} z-i%ZFjIcUK;rIqE283R?AN4K>{78^s3ZCglb*^h)UDQ6#dQYx5AHE;ObpwF^J1(QL zLH`UkE<@cyfMq;Q95Cs2R;%I(jmyw4XvF9UHFml!`n8S4sOT@8hV^0TS01vLbwT6@ zYO@n2hhtb)53`SA$w zj^SY+kPqoedTsf^F_h`k&hLyhe+jE?~ABTW}!8=CXqf+AGIJk`3`B2$P3r097(du=4;`SI%J_OTTY|0GFz!3 z5{D1n*WqD37H&zxJ{Y}u@VDu4E=cIvvg$VAk)FUpScH%0C+Xwdmrd^-yyU`<=_6f9 z-zwN`*~dO*F|uzz@R$&O3+oB#4JP|Ij(!co1mIK>c#?|%pO6uvK z;bh;;D`pmb*IpAd!;*dKY;Ab_?v0nvxpCeXzsSHP*pKG?-M)vR3%q#*X`GFR>oC~w zVw$;*^B@w-zT{BPBM2Mn=|$N2JR5<3J`X#*r|}NgqaMR^1kZl>rSa^-(~IXIo_p~u z!LtBQ3vm8Dp0^QC9nR#9d0js56oel1n2U3O+1^yce=_dTb8z+u-Q{qPvE1+COj~S> z55nJq+rgf|7L4N&|0LW`VXTn$W3azt>_=dK+t?4ne#Y4M!~T}Br(xf3?7Lt;ZS1|U zKWXgu!hXuwQ?Nf`?5(ixG4`dfr;U9f?7rY*>@m3r=X_w_Wjx9AbC2h^HyA?_lU$so zz%$HKT!Vsl%FR2Ku*Z$P6n2h-^4lEC|7|lqO8--^ml_=3wBgIgCi|yVB>TIfn|$JM zz9gQ|=a=x@aqMeUe$!fhs>VX=ykj`$BCSAx;eN{u_Yb5yd{k}7`=9; zr-^spqAYMO0oTZIA1q{68TazyOqgt>C38ylp%UkZYOYwHVSp;8&zQ!Z${M7Tc>e|A zr93ai(1+GHa1d#j-jW``xw~&R-}ByC*3B@8>~kWOcOZRJkBLYYmPbL#Ljg66RcM;3#eP@l>2VFJxWz`3d&~;yjaKy7u4uqSlLg#kR+zpq*kz@GDUGO>D*poePBw5) z8P@_5SZ3q#J$4(#c86nh0(8$42TZ!Z9C9xXx!GO{5dW*FSK)~%DiTt1uGpzIB_Z%k4L=6_G(+de)%13>n*P}8&K zst#s8jpuYe@jd)Du8m^;&{Ta+n5xeSv)cH(#;h@Btua}J_?~A>)=jz_jL9;kJ88^D zV=ge}LSr@=6K%A@Uuw){#%wWWt1;Q9sRri|HX%#Fsp*O*<#>@jAqF?SfV&zQT6 z`G_(17&C3mCyn`(G4~tufH9vl=0Rh=Xv~Z;j~Mf)F<&v}F=HM#<_Tk-G^TINQ^q`P z%(skr#+Yv#^BrTpYfMy7m0xjVmKw9%moKnrh@9X2ai%(CC5k*4AD;y~ zbrS)W3&XEBrs!miaI;+zARd7VfUQHdkhg(Hnka5n&ZRKh@Q|+1dj~iyRso^693D2k zjcDc)@FP8t4(QztJLyW2;vgV2;2qev!_IgGh__T6Fy)=Cu-ozWB3>7MjF&Af@fhF3 zco1g_h63JGb-6fTN?bHil>*>6Klia=_(8P+q{r`KobnB@I(v{ohiEY%^uoH}Q%KbJ z1qr6$>2dTm=dngyhRuc_M?a}Qs9+pStr1A-KJK&L@$7TX@$3`mBX^C~Ys72V-$wsd z){&dF34c8pElk*>Je-Hli&HC{o10a80W?ampLDz{3tH`K+v++=4X~8;i<5{r8Qq>2p4`*4> zWV(Ni>%{X3vqsg-I`#`Dm`Bo>nv47t5YAy9+j5HIER;*+Gve@}`&vAd3Bo~3U?&d< z2)#M*u<12`9{Gm!_^!}n-(O@5tWFP}BrOJn9_I;cdK@cp;YWH*m(Xj4-Ik+A;4Vgv zz70Gkq#Onx(qnqVa+KpHpM$@Eq&+N0S0R0!f&^3Wv>aW#rnzlnduI`QZLYpbW*>%^ zqi^X*0y)Zc;t>~Xw4mQpR$|P7=bF9A@3Ung-2$w0ziF~g(oU1IjBO!--$L1i?q&ci zQ%AC2$tL4~lC`ew4lGn_Tc^q$y?9CLsV!H&0z6v|tb)53 zIq(SZ4&Y(mOwuUy?fw+?plQGnkhF*8zzzg73ldDh!`S*vso5M|A8u^@3fXd3#MR28p`Y6e%89wzh+z+!PRZ&RhNDP&qKHQa2E3L zGt5W>lD9O`WCYLx8z1tt@bzGPm_a!G1%zIh503$1wjjY2Jn-Qav&eu3hU3Fo^~KL` z*Xf2O;KNTq3&;6?1NEq0II9n5m_Ja0a~F}C#RDJbJ$$Z@d;YqfN8j>=atG(8;_OtM z!E4i^S}=rZzrFhRb_tsGbhviBPVnC7wY_0v~g893zPStmb;sB z7!nBHMLY01-{)DccsJ){(gRU{=NqMntIFS$i)OmtC`b6zdbXK3JCEF58nc*ZotIF3(zo*nrr+EvoVNCnOe-3eMheWNOK2zZCz;5obHc%Sb$iEcf2 zfN#criwW=GTv&X2%;`f|5~kNxf;DbskuH5MW-MjS`b;+UUk!g3=Vp!p9W~b9)YK1o zSmt;SVQf%gP>;rL{w&dbnTXLl>TX09?7Mz)lyCm^|Unjlj z9?wS0$0N@T4ZIV*raKnB=J14cW?wFid4)9Q1YF!ROInY(`*PJyJ-M1pPp%ecV%Fho z-g1Ps;=E1pEB{*}^l7C7kKwy@|{R`n}k6MCA$3!=)eY zpUj7)br}6i&<~RQD+4aifVZlJQ|g{Q%W_O{jXJ_lP6pp$O?)ZygJtG|Z<^4T(r3*d zQsn@?z&X|!e;r*Dm$9HfBGy0NWs!ZV=@h+Yn zePh-Izb5cK8L6vvpXR0sgWpczTb@@#=j~MirZq3`9b3BuJTWXvC{S>|v zXTCCB;KRx4d~1^qr=_VMa;L03gY+Ox_Z#@xv9c=O|Q+6z&&8V`4zR@W#^u>sKyTkuL&0W97|g_=J;y(97wx=&A!h2xk<|o zr+cLJ#|!75;*QKGsk~J^Iwab_VDn!xpTzu*IwcO|n!|pQhLWJ2VqQkk3!863nD0^a zWo#I1KCCc&40He+*sIF}_Z<|5Q=`lS_Zbw1V?e?J?kgw^|CsuoJkJ%*zH#3{A^x}! zK3ncQ@XSOTct#@gK>&5|oP7o@5(tywY+(iHeo`DT>Hf2j8$u%hhT8&??*AQx>i7RO zc0Fu3q3(9f+nE*rbQfM{ zcJAe4VT{2pJOfS6?%gT3p`Al^C1WqRBQ1Ev#OO40Vv!?eX1);8zF`84bPKD-FoW4x62J<$? zKV|%>-_gH|=BISM<~&Ou+|-|U(KKUv)t`o}WPHF|Kog8|j3)faL)EofKcxO0hrj6I zG+Do*NPMO7x80m4QtlEnzX><`I8GD{x0EK_%V=VtpwqZF(gbtwGVVucqM|-&+)vR& zWX~D*L7M1L9x?8tG%8$7E&dt7&kxsI5Z3AamWaoe+&E!cjx`itp?$+0`|!V zh8QUnkbmjIEj&c_eOTk9tOsDZU|e$+>}<~j$bW<+9vhGKW6vOXvw>%iQyhVVJjSso zu%+T>u%kL3$)51U>q=n;?f*rE!(r(n0~Eggd1lfbj-by)P+0t>w# zgy^*(0M$DH;t>wvvFUYM`NtMo=>1RF?eyI{1ijZo^m;6MY{7-zJBA(%1&<(rGERVa zgtzh7>DvW*UGQW6u?HaZN{~1dX$oEnR)Lv{8rK4z58k>MZ$aEEdgLF*33>r+M4^3= z8DK!H{3aW(5`{WZ0ud%wGro-mS{+$1?6_9FccmGA7=C=4WB!0(Jb~leN>B6CS+9xf z;Gic!r;kBLfi8_ZDd^UBFhrcx0M_+YIUd)(-3a}oXCl}6ksz95@s-tP<*ekaGu$sYpb4=l9^fNs^jFsB`Gjl60k&_SW;w5?mW zX06t#s=*&gptJ3-qzS%azDB`^=ha4{D`bufHBqT{=p(mxlKC$nrN%bNmI+5u$)wH_ zhYzy#oU!wb@Z-AzLJwm_1@ul>^kj^Y^uB_}mN8GlU5t!54!mP{AiC7UcqP5I?06FK zPDPl2$d0g#IgNn9#?G~jNrBte7IiMlj2ZQ2sC{@Dvyd5K$(WB}Ht8Bye@_GM89Bpq z>o{i8(uezDu2*-qa9pPqD6MK7Zo&Y>)T9@^pJQBX!T|j_Zo=^uJtnk-;TW^i>yBpt z*Wr3w@BV&_bqoY}siuCkOL~1S#{Q6hlTfAJj3$@((bP5{ZbzT*-s1v*U4_ z`v3my*{7?qrdh=c8fZ7m%D)CVNtF(CoDdz-AguyA7~3M9ckw-~xJxl|&0&lQjvsi@ z`(YNvGl}1bZhM)!*9P)5ST~IHOPZb3&AyZDDNHlsiVqQ22gVE~&3}eFik77c)BG&_ z8yYCg^T4r~Lj2JRpE5uLd#ZWh+GHCz79+UK zTdp<6QosNhRuj0iZ1FV1e2E!dA zhA@@f>eMWEnzfrc`|Y=PY*@dFHtOaSRVvD{YM^IDc$SJ5Rg+w+}t& z6ci}_x-G6of>$BQ)ey9uG@;+_qKOXXQ#A3B2aW$ph_0-C^A`akeHj6d~z z`ct-(&T}*|FqolO^9D}Q&4}mM6 z_Hh3ugYt)hJH;g=3!xUOKW8Tk}wU2**en&NMlG*-9^aZA{1 z*h+*qGJNGXtj!(#9Hs_-hytlq+BMkPP4Xmx`AMBF+=hNyh3fSHT)S!WzIs=m1*FH- zspM%lo2k2Q5A5c^Ap3Ak(B;?**KF1plj$C8?PeqWAZFi-1HNmJR`6U*>aEajB*(If|60hqnmkd(`$vBbx1(yCE;Py>jgdX z1?iFdgx>GMPI>}qSUSZ4Q|K*%-KO^l=(+GCJ*G?Ot$^K@%atH*^QOc_6C?s?qz4|# z7CxlM^oHefBVOWMqkzy0%jJW>TPH{`1<%Ok_3d|HcBzQ9oAp-~Ken||XI3DWFT$6I z-pRYF8x~kgtFa~`v zoCc-GN*b__m1$UDrQs;_z6PTU#-P8(QrrB)@C&DDbcn_a(D7KD!jI$;yA7hu5@SU}B1m*>GFivX; zuwJ?1fJyhXkh?zQ&bM6}Kdh5EDO06Hi85|SYf#jgG(t#wQp!_EM(Y=0;#tC10u zZ`J6mwa`RjJ80qq?=}9rjDOnrXJ`Wd6iwitG5!<+^jCTi{AbY(I?Pwj6H+gtUbE1c zm3S{W)Be2MH?6lww&42AFVj%`Sspxl4G^?l z)G0VG<6wPa1Z_|>$-9}IvIe$Y7(2zB0>(;rsr7*H$5<)X5Kb7F41cb(i{K7z;+0}e zAo{LPS7DtR?{>zwzHrtV)z3T6QMDoWv3{+IYw@~qXLk?U#lvddBG(z(bcxGxRnGUY zefwW%59nrl$97M(d7$&R@F)G#FlC+L>y}+;_*j>wzOO~E1;h%R0k+Nh8y|Y#Z=5p7 zH)75^_(q5ld}E3FMu;2zjV;zUHVyub#AUnBZ)UY7 zMDWc*hlNU>wA)I`SAmnT*mGlc*jos**T{{uHk5TX51N6+&3UrIaDDH}X+a1^HnhiJ zI+(P<%%j2fpl}XWfc3pr95Cs&>pI;XCYC$Z7zDv)!On>I*&X8zMIee9v| zE&~qb({Om6p(mU`dMs-J;V81bEf04*CFF=n!0I!F?^I#*fJ5c!rXX%uj|3 zu)jUh2DuWkk2XEyA%E-Liqz~7cxZ{sDFXIKW_3_sEnSOSYD4wypkM%Zn7@gZbj zEAVU?*aLSleb(oI$AqxX^I^P9FXNPU23ED!qJb6zlJ>9+tU>xZ1qr6$VGrj6Rf!KqHwPR`QsnvHEs&!Y`Gb?T;k>5L%9Aq9aT!3|PP91(mP(KkO z!UEwK?L|3Q7_QH^=67ijPS&o$`iWR-qk!*F4zm9tK))-DgLy+aI9~!`s(6f$gO+lR zB$VD3L}FndF?eV2P9w{NvQbRN^Y0)dt>m5~EPo9>#}99hiiiAUofYnw1-mLsm;~W6 zRs~xw@towrmsdNj#by$F}8WFWkk*&4a*WLdb`FNRR1doVMIN2%N?66Ogor9l6(`nDwdrUV)J)7Yp@ceJ5F-hlNA*7f?>xKshM5TCSbD zUtzNKg2IK&V)2NO(@xdZgEu>U*5$5vm-%<$?-VhyUa+3q$dW;W$&5PWqg}@zL8Xy8 z$WoR%J_mNGPq3=7L6;T-!V#Z`+ot!FMUPw}^eEG8nXwA)Vr0f6z&nPA{K<#(B)zuG zScP~8qZgJLlr0>q6p(L3U+e0tR4+^a4BOYLZ}2qc@N4^C97Ja5dAR&K5X@7>Fg#!O zd7R6j=Dn~_06dRz!SsMSFWh_S6z91j`V5A@rXTxj^ou1m9j@u%JQKqdZc@_{f7T(p z?^Y$50J9U1J!dqBj531yZA^O}g!{D5VDbo47z+4Z?1zQdN=!B3G{{fWLhe~1H+x)4 zFlmWJmDI>7tO97=kb=a5W#uXeuJvsaWOj+O&8!CVQYYsAE9ybj_@E}5=$ElB&}4n( zSRKnHT7sVsxb{wxb^*oOh+1pMG8GU`AiLUf$VHv2HGah5!*XcEBXu5DwZ5M33JAR< zJZyU0<0x{+mTQZEXUnzYa1SPj7^fBkumIxl@L^J>F&@S%^liCz3V7rf0ZDsUt}O-e z<$?rL@PfXOx~+Ej^UUh6Dt_LBch3QWz7Y3h@q0YA#vq-o!MbJYlbnO6OyfEOKZEf# ztRaX%&L!OY`=gL&iRAq_JL>*iDeir!#@!3$&i%iP_+9;#4($H@In-6PF5z9AX~Z=I zs7I5c4Vc%*`9@P=Vmm`wv;HX`6Y*QH=8MMfXSq=NFa45f5b|)4=r&sChU$ z$)8Zxl#YehB%pe#H3>CX8|^`s%6bFF%{Up~0Om|cpY%>x^YiqR_n0xo^b2cz?1zgmU`)?;t2fULlO0F)CWO+K@3JWJ-(fWiisagS) z59QjoLj2JRpY=lngro(7?N>rus(^XQwQ}6AEkHV0>k2-Wp+Nysg0c~}X<^Tq>Ek>;`%zpMcZ??H z^H0;nV8z=sfkb)4Hivu`?E68c@qL~PqS_>6J7p2wNt&$e=ha5Zz8{qvgYEj!*;FuG zu>QggtRlG|*4Szt;wHP#z(bgYr=UH6o8i|PQ#7mwxXHr;QnTj4!)~9BpwaN)$MR#> zfq1NwvYt-pB|(d2Dj@X2>*?MGJ=P1-V{H?9t6;a1qt2aK06zht*9s3ieLRQVg&*lj zd&l%`z=Nn#Fcjt1aR*^`qx7LG9te8Hmi! zq5AaXhq*jb4GX@SD&oIkZH90xvxq!W8ng-gF81lkp90Db@prfGpVQ{tt`{Qfem7*5 zl4W4#e_Q`rk-lJE>kmsFF%QXplFO7w^b?SJTn7)k9{1uU)?2B=#H0HQc%;t2s^+@+ zu7J?{96W4#eRzpwM?a=T=q-fZmJ5Aw7b6!kz&n74O_QWi=!0GWlpXe&8j|*~Tu38e zS@2f=8}o8OFWen|yx@wW=Vo~493YSjT(^t9CeO`$-!sCQca#SLbkAWSj37OpWs(Qs z3hhHZzA#+4Q^LtUto{zBnD%4AbD|7}^eieobcS<-pBoCBdqNkD)<(HceR`QAM zGC_xra%O6$`Xygz`6aW(MuJ&0ugyl3SzaL@P4M4Mwpj@Y3AvB_oUqL;Vh_u^W2m4a zpNPYU?uB?*cZH*dl6MP$W6LL=>ml@PdDmv>!B8-iyjzY?o1PDPF8spsZXNu7-SUny zUjWHa5SDjTsy7P~Ou_r?Ot1b5Jz=AMhnIJv$PGWgb7ByA7p$Fe^;#MxDeyDvYv@Ng zZ|iS#3$Tw7cF(DU=LqW0+36###UYkg)3N=%u4~`E>;O!M!Beu%zNoS zi42_&Kbsz9VHtWm{A?Mz1MXt_ zj4uMu#fOpaI3C7$nci@p@g#5>;V;0vAcSRT3(_a!+YA>x$k3TH&qs#NoK!V;RCt~yW$15fRQ1bv*%uI? zoArdC@kcqgYNz^D=?o-M;iAR6xmr8u)j>>&IJNqK_A;M58Cl18ZdX;w)(=tET1h`w z=my_<;ifD`lD;l{!-5yC49i}bClAYB>dllR!kt6O-Y)~kmgSt+CXbPxEqm{TTjUa~ zYK+dK#ek&m4!CW4tA>!h8-e%hmc73(iGwL=56fPj>C-z19%Szo=OcTsmZjkC@cq{_ zIx&Ilj@EU~Ho?DRteA9G(Ch)mLVzp(xd)nDaU*huT2l5g@LT%mm^OKspB zgUD8X3uR+?-H@YiqcGjY=G0y4zIn_EF@Uq}7U117j!5Q3z=Jchr9hj4hVvP&d)8wM=ec2e!8;+&14(C96LIUe{kiJRr z12|g*?zp!%H+3@hk^r|G^O+9jFe_oN#@U4lgpI-)pzk}*W3)ffqVCwjU0r)~5AB@l z$KYNSampAL@7jy|$go}rXOHZBq}CtPlzyNznSNkT#Mv(AH(!IZ54paF>w0)b#Rjm9#F<|F7Mve~`{HgJ_!d}@>16)!d!qh*zT^BD-}uv9#5sH_=G*{x(?G;M zbn2b6XJ6KLt=%>-3TaXInziiBZDCpB+Z~+o;y91yVone8V{ZDEEYA6w zR_Y=id_$yo38askk2JvdNS9Dfd?j);4Zb2)D7;KRJtB{Ho2tl1`l`1)yJDat0i6UB;n`W zl>HHSsj_QtZnU#>;7j0*Rb2CheCnOE+2=Vc$-cNcGn4kwOsAL5&i3NqL%ohG;`R1d zqD&J`oUCs+2Dx7?pAK|WwT(XJB5 zoBd?pgxqXz0_x)Zxk;E;ECmkpg1oHG8NitWPNq-Mh-WsPio0DZ&Rc*>9z(lW#(2lL z_@-nx()AO3E7Iv8olc|XFRWQ6e|-(_K*lN_ZG%|_-c-DRGfExYJr~Pt{wv%tor{2{ zsvsj6Mw+Oj**2yP_~iMBJGWoutxDJ8fp})?sko=pCGXq>{6tdIck$lNT{V8xS(3$B zbC1=uAWxcB_K!k&Vcclis4gk5hfR5H3(5;ipB^hsAspq!bL;H#!VphEd7-T@t43TX zH=H%p>+3T61E| z?rb|1Pi$6s`%~sm0Upme5fWQcqHcB z=evlGQxq$8GqY;m{ZJ4gfknE`(W-=S$hFuW;s@t*h2z7TFNz=zL3_B_50cx;o% zi%-naa$#32rsV>7B$k9sM|~o#9`H_25^@{#VPo1+nCO$agId z=od?D^{W!dE7P8Bvff)@-W#1-;K!2N6#dcWUCM60Lt4-^g1BbAclKq52_A7qC)3ZA zCex2!j&GtYW! z)>C(r;+gHhXMNUn8S*7Veq~+mLS5dfaM*T`r=o7R|IY?r$t6|)Nz=UJKrWeg6mNUr zG35U^yi=9X{LTEPUF8ePF@kdZW7Y+H8_QA31O19;;!WwtA4S*{^jpAxS)Il`kn@Za zX-w`weEK`ykM9;4zWdLhCG@+`YQD1LM10R6-pB8?@c!ao&%O{YE0ohSD6hv~`qw(@Sm>3Ylq0raDS^O*Vl)cqXY#a3I?nw!<6OY?^I*7mz} zfO8bOdNpv-z$o)%i8kLFtxYN?-1kvQ6L{1K|LwYw(w9ZR>%&i>(+v@Lu2@ zz`HPlGzxvt3!o11x`8X7_H*<=;Qihpc$Oa6T+A6MGq1d&==*kBb>$dT54_jtf!MzR z8Exr-UV1|Mz}}pTwyVy&e{`uETW;Saac`=&y+QV$fJum*POQq;4k6YfX9i}ojy zS|?4IWLTmrq!Xr~6GAWiWAyvSLf%ofOIt@b+cjwyAG35rl=aIJF8It6A z`u+FkMx&p~cd%#sno)?0^zA+?`0s^(FnZ!!M$gy^7UjJF%pbNDQ6GKR_t6u1uQ&U7 zq+i{#H@6AptMY|=Y7$HvbVTSoDqTzu($4Q^x?X~wh;ahAb)KSMS5^zVn<~^1nOBaR zeq8B@4`1egjCl{g68Adv<3vYX%yw-uaWIB6i8`XffiCDkH!SJ8&PAJ75B(7O;&mSS zZuRW<{usPfgZM_{PW9gduWW=)v~AI5KZfsT|0mg3lB2%I{*Kbo`cPKLm)TxP&{vLl z+xj^kz`m^T>|&!QG7Yw#xEt}+b>Vv~J@J#&6A=fR3X~`H4)kSQ>K(iX{-!L}q&_w( z1>S&eK)r+MBJW5)LhBw&2XH28Sp>T7gUCnfQ=^d1sIzq#-Q%V#&KBLyblUnN_AqU) zfuGSAC*5oG59*C2h@btD_tOcNfiI{NLc!BxHqhP3PmTlK75)7JoiL$wSN2aJAC*qH z9wvDqkwF@;Ho|duXF0}Ta0fl>5&22#k?4i(c%SJD(#Afa-QPstkh&+omG4k*b(4q# zZV&k-dLi1D$Ldn3zg>HC9FL)XI5x?;b4!+GReC_miskf2c($UfzJ;>7O_kNy7K9)8 zmeT3RBCq0ZxU6nAWp%SUUzcSyR+ZJaOj!-3+uxQiE7T>{8_E}!RUB!O@`AotMmu?# zWklYz^?UY*zmNVN^NM_=bxC~7AUfh-m^@|u)_8;KoJ)YGcnJJ@61rp}q4fvW8`g_& zAwKd$E&NfD6;H&xOOLh4VomzJEiL!?TfjrarJQ2jQ0*4*H(PpNE83BgmPVg=Y!fLFNt@qIHd7boQEergTrmbrHOvPp1r=9hV?@~UbtamSkt|{RXAMr*(*FoF& z_{Z^`Z1>a{4QPnI$$H})KziWEc8mUT@Yi*N@Yml%4^m~y@sZrt~$8;W`=0 z5HmiM=vI8N0r9d9D86=}&ndkVV<{Zla&y!DPo6CxXQrTi&}XFJ?o@nZSsm`8i8uAA`JEb^-&qZ}gEeF7?tx^t z@^|nK&qj$DQ$D8-_z9j@kOY1c+q5N+@0{D%m79sS(n(V0FB$k3=uT|w*e>ziune>3 zua_X)K1+q;J^~G}G**=MjKuqEY?(wZBp)o4ga_QKJ%(C`HY>mM}JIMG4JU{kP}5=R?#fc;dHBT3aSsZH?1Iv z){0{*7pAXRzkYg-p7)kBJ5@er%7E$Nk^Hu{PTMH!02;%TzwlBa#*(o)n7j~AZ zfY57%huuCb1U>RI>DlcA*B_Iv$n*^CLIc*oekbfAv+19~!#D)wojc%R$GZ%KUHCCx zX-60zShJvAii3DD?b0mZao_Dlc%R?HcxjThd;_e`ljzrclNJMbMM1b-x)(1o4+R8I zkBhEb-oC!AsP!>d$~ELgtd*-Bw7R)3kH61#Z5? zIwtjOF#bG-aERLggkG3G{|XrPT57=){#?^q+?f*fGm5`QmUB{ofIqo+Ji#-~-SmKi zvwWX&x^w&8ZgsZ=5oaUp82djMcbkQ?nV!O2r|0j>?J6(#_d`$KwPu_Dos8>8+=u!{ zr?4Kt`F{U?XH)JecT+AJ$qXDrpNe!Ye|yo=%fGpMA!UWF+tDpR9fxg_KoczLE~2fm zZ6mQiL|K=II9A2l=@+S~G1wm}#MgU48v_F5s64PgWP`mIl=;N|4cl=6@*&246)@k} zUqRgl3&{zZ>**>u{71F_X!n^0w*RDe;rAiwc=LCFZ`7k*41 z(rZLu_NSJ1`Oi6r@cyCf0ryar=g2XCY`GYg zgY)IwH{i`7Q;ncKp3t!^5YEzGJlo9%p4~=1AZR%Se2N4Vn;FV#U8~(%9$~G7l2ase zK6(i>q|WKh%>l6sCUHKz^$;v}^1H|g_FD#%5pxV43LZ63+UH9dzU)FnO zH}Pn{0gqkx55Qea-S>f)#=|)HkltW*zY8B_^SjjPaNU0j0azCofZ*lpzFOumY~8Q- zia)D@@)b}}_tm;rjFWSI3Tk)R5{xIk@`ZGEkDKuqy8AFU+2to2R{A?%n&KxB7Cp4t zFMp}S-!Wygzj^Wsf2ZsED7k@A^~qp;IVUM1^I=*Z&TymlnpZMUDK*W2Y1w{ zz6+T3gUbp{_yP`Fe`xEbSl3xMY&pUF6R_*Q)FFnkUc0my5WK75w&lc8yu@+|%L$IB z*>d6-+{MTVCYEtC4z?vj$%zBNE9Oito@sF_=m@~6ATKA@t}dpJP(P#iGrW(5S1>JlA!0pFRWj+z|WSQC*dwe zcAk%ZRRfv=lJ;;Ps|D#x2@*`f!(86g=QWq-U0L+m4k^S5DClFwoWua{KSf@bss0f5 zro}M7R*rpQSbx!vzEUKHv%cx~PndgaQDazrxkzQqFHda3`jN|j8L8ToU6@4w%bV;k z@G%GIcKb2!rEG;WA&Z{Iq_}qgYdNO+w*Nn4tqAwC@DA2Q*PoY0yfe!FF5-Y2TXUeN zz4zXGT>$pIY@LmL5P>IQSusNA@hDI8pwCHtB=8W<)gFWO$uPyFfZzE4v-dvGaaHHN z=$SvV$FdNEtpwE-%`qs6>liq)Wsr&6jARj<#7V~B1gCM4WJ|^_vaL#jID}iqmJG%i zD!^-9E``gqA+M~ito78y>0|XdeFJtl*5`x%Y8BBAb8|ECXb=gI*u^%DN5ADqxLsFqHa!vxV;ya1X5IjDHCJlgy)^2bi)$H=dj8{IxbWV||t&s`8TX z0;F~6fJ%FmMBmJOk`I`&IoqGv7U;Kh)r#2ELoeoT%C-iEhqv$0`%U-_lmyVjDBO$U z9au)cPV0f(sH(?3YDq3)`3p#``Q;*+v$4*JLqnd>1!MA*8oRA((dOmC-I=to+Nh;IH0cO0GF9gPu%7# z4g4LamI^$E-wO_rrzm|<>8?LN-=JTBJfzlm1?ANeMAbbgC3#0$iN>hJD`XDEypdPn ze7FZ}N3EVuw=43BL2Q00Nfg?zxCI}OaTuR~BMpB4P4Np9W_9?6n}{kZg=&d!m}8aH zAp1W1bOMHedo;X$P8^y_d}9s5Z5RQe=jR*WHt3y?Zw!J^5Wev=@PhD-LB#7w;$Xw8 zz|S`h;Xx(7as9HkHd!&^j>hTv#tnh@GOnq>H$t&n3!&KBWjNb%4dy?~c&`I&uvXl4 zg1O7%p%B0KaO$y_#XCk0sr-%hHqwp8l3dau}q8l9)GPnSSi zln$xHV`N^!wn!dhmtIzFc#+R9Jf?4WxbN$l(YPI6g~(&~g88YvjF9);d2gq!rHl26tF&y-J| zhd&6Pc^!Be7|JG$hw-v~(hYhZa1AX8pP_ux@4Z#ZBlwJ5#-uh*Ps?X6@(xWk2TDRd z>2>!mJ6}@gak)MPeuFNB`3~koNbUlj%i+9-=XQ8bNuR;dVL{~_bIe(d{G0Qm2BSQP zS8FnmnLgbDN(m-EDu1r;E-vv}j&>!ew7&;yi3(UQxvpek)LCXCQr93T@tn?h0-=CzOp#}Sdm=@j#ulOH4%0|avI>Jym zu>JGfU+uKPA7`{~@9{-5t%iQv%|I1JvP-51VmXnwNy}h)vF-(gw=F?{$=l33FNs4F zgZVU!N$)=R&Hg6zIOj9zZGrs3I;D)yvYvoTd5N`oe#-wYG~QH2-A&qA zryk12tlxniJ(+tHaYb(igXokCCH4x$$e4VhipSY|))Hsr1*p2K%;%l$Rn93MDNsPiz%WFGgM;mi!u zZQqRX<#2&#B8hvD;r8l$E6z>+cifFIA9kISehlY5ANxc2tvK$gijNheu%obJ_Tw=0 zm*`78Jb%OQp7Cz12JBajVLxj<%C&*-BaRsMSTs0K;Y|4Vij1!T{urLUZN%Gz^WGND z_WUBA$DDn*YYb=2VaF2VkH?*TkE8uP6+;-`hab-l6q{hjodb2v&c3=P*e$T*iSc>x z&udPMhizwHSlyGyblV4D#)=IooMVN%egb1@g6^^6Y-e@$a?~MM?Y=1Pl8D70%7*N4 zK9<^@i>4kzS{~!sQ{02Nt0;NK9YI*jDCIkfyZB-$yk}YY*;Xnyn>6^2#rJo?JPEqH zPzPh`F2pGAV_+Sf!d!y&Jb`r%e?9NTGwMfG*?#f+CxL$|>P!^l)`R&7>oT)9i@h+p z-*(%|{S|2Ne0L1zX4&>|CiG?2OU(LSu>oyK>c1hL!Ws5m#mA&R&<0d}#NGOcqdwq{ z+xs48eKf$Y>H}e_KIli8Hrc2T8}(tMK47=NR`p@K^Hh`(cz9aL{I0-XSM~JTJu4Z|l{jiXdQ#US$pO|CK*``M(|g(;xoeNTby?w{r=I)X@G+j>9F;y1V<3KMfpBOHgTE&GD1K zGND}BqP^;$yq1c+%r&mukRkE-l1H?Is6IQXb9b4n3QqX(1whgfQ&$UFKvXs0Gl%JAqX%G zKZ9axxg7Eh%7pex+6Z6o`iESb(W_f7^~dxXX1N_j;T|n|Fth_PPo*_#Jx6($ICpR>Do%JnNmEr{iNd!z8b)8nA}4 zvBYMe3v9oJp5xY6)Hq>TwjjVh1GArgf!dqOVS{qB)M@k?)N{4XlD1WYX zBB#9p*Y0bAePAmf5t1uoUU~^pPb?s5te%iHPaP5<@ zIc~5F`0Xb(4uCPpIMBgx_&G*ixUQruTP0-)GGu0~ibFN&5hgux^1NKb4l*3pd-AfY4io0Fzz<^jLSK$9IWGdc82DCy<0^y?9^?y$x`i z^mc-tjWE(99ig`kZaf--rQmtc3o-^N{WoyY^pqhz=AUtbo(Cr(*WB#+N@@3xK~Lf( z=mtChX$3L{ZQeRK7-&!RinhStIbH+C4JaFf4mrnhXMH3;2mBdnK+j1Rcy0pY#i=u$ z7+;`k2qk#eomDS<9DS?)UHi3MxC7z!^9t9%Mx8C_$DA*`cy~dCFL+UfAF_|+`f;xu z*6RzlHNOyr`wPI0!i_a++-Eqy0J)$(2Kxqv+q(;V2Xi$wW_*%y4ZW6|y@mIWU0L`T zZ0K$KY1iJPp?l%X$zhQpP?E3ymhtDy#VZ&j4!Wqv; zthLm^d*j?n&>=3z*QhgIbnyP)0k?zU@RJ6|&Av6ZZ)N=e;Y79A!d}V_ibA3pFSYZXUdQWQ&Da-e&FU z=^4?_Iop#Fx@TnT_N^G2H-D{f@GeZ|Mij2X@LWA3+xqSr*m$>E+_-aa$Hu-vbBBl? zb2)aiVR6nz8NCHe(V-27x0*HlSKjNz@I$8*z* zXtVfda!%1(?{Evk7`=^xsM^q7OY6dBzm;)u35w4g7l-hGMaprJI5hNk!k{|5aXSe& z+p~bs`xOM3^mgJw0%4?Qj@!4xZ@g|T!T>@%0ikyr0!(@@;DL=W(qp=aN4p;;VvO5d zV+k^Db8Vs*2_cU|Q&sdTjoa^lc%?NA|G51+5=7ZlK=LT#HkBiR#_fwe3+3)j;a*yd zTk}I!8ae3AbAO7>xgy3M?q9JiwMXusE8#H@EEjVuU|$qqd+KucvY6p)Q=hKUJuc3?;Q`e|WCDdL1^wi_ z(6DXYeivLisBGS(+AZftq(Pf$t~CE)yqy0yw82|1jCYn&RbroRs;b60YfDcSgm>bu z?~1&$8^ugMAgw0}H|s$_T44eKCa>e&6|7G`@8tTg@D(`KS=|I3E(pD~@SF6ex@%%1 z@J!x$2>xqbTC^lC+DMWIr|Cjq11uWG%k(l%(DUFlaBlQ`C3=3|$-5?oE8q$5+}^h> z81MY~9%7V#)F;PkxwpiprIqLcue1)qx+GBUA}{mHwF}i#{ikq;2UHaa@6ukbCNP*R z%-BGT*Z9tRv4@zkeNgrFtP26DLGlu_pHH=ixD$BjiXNTKu19GX!4C9-{WX=+5_wBJ2WHobR}}W z$$~Bf-w0H5diNYtjC(VY&?gZO4GK(pTJPCg#9>6$FS>!1E z85r^tNu#9KltuR9#l^r8khJ@G3Uv>Cf&^Regr|IMtG+F8+Wo>yR|M9-W_>(`Yk=5; zrp`!mu0}m8c?k7-z1RmteIDm?i=b~Lo;Rlh9(9iqyD~nr<>xsixj@fRWd3G`x8uFl zAE0@i!-7yClDYO(S|?My2#;G~D$VPz(XXmKxRrE#n6#-TBeD^=3jGv{HjfMp=xH58 zd_ofA<$R9w586!gdpfUkQ)SNURI;kb{Ax)j#`+WxUghU~=AD1Up)q-%w4U_{XCDy| zdIwz`yPkuHme zz_KEbHT#;1cbHAd;eyb+6@HW6DCpS;^Yh$I2s3%^Yw!o* zxlts7>tk$lG>n(|_w(E)#G634fTZ2ea}NM-M37($o-4mX&=}YmWF2BfyPVLN{>;TO z9f`+tIVT3MJm>pD%%1@BS!;X`UD^O?0XnGCoE9uY0WrzP%=XASw1#zYE6$|d*2w7a zFfQ;>@9~T)-yshX-g04jd6)PHR8})3HBS}(9qurBYgl)wU@}I!v&$| z*L`ikOVSTbzTww>QT7Yc&-Vgv5{7L|(kS#z*{TEaQsx(swEO!xb!6w!eGP5v8{M=e z$h{lO!tIpL~kDZfzIJyY;yu{z|kdlMRzxLe7U}{p=<7T_5WG*UwizG zV^#^II!=;y1-?-Z6U+nWQ_4%z~w^?e2s6E#9!Jfgs?RRe7zIg{G z%Xe#eF4m0ZE{H1Ufq0>ly>xhtKs5fG1b+ zKKyW7Bp+V*Vj&XW&Hetkr?GtANxuNgv({uR;VC+e(i*g{(jADb2Lkg#9%PO5Y}zsO z^^vW2Zr3wD@!|PJ^=);3nMbT@9s5%CYxY+*ADJ(;!_6`jkdo&!#HRdFX_{5%18WmmASV@((doCy?C$z z?+OSW&YpL;i+ut6Yw&r{3p$eW{ua8K=)Sd2FAtrRhLxNdg`UycNDa*a*iVHn1wu5UtNJ`W?x-{mn!wu&jRng z?yIKEC~5ciRnE13jTv%5@UCCB)NvKRf%~AE#gLXWue)#%5m|!n0Z%yV-_HfRh^VZ1DnQq|gDCc!RVB=FJ_; zHxJ?9)X2!z&F;NmJcQg+3hdW>0tJ0R`VsfrG$y?&vWrW$mS2fquuKJ{CdeP8FY(!P zFg6`72wnnylV2RgORQ`E*uEBFCciiWe~_{Lr@)(pVH}jl887Mk$M)wCZ!5wDB<+5F z!Tslbf&^Rew2pIxhZ%Mb1<`#t!Pk}6RpJ+jo9pxU@$U1s7YkN44L!qcIV-!ni1qoS z)QdzDPvqc^Z2(RvgFQltXtoh&Wg(X>`0(7$(~aMF_O0dFY+?2{KKAyNbJw0G4r#5r zDV@9j$g#rNPp!&d{=;L1u(K+E_Cxy%jkwz*{NY^%3-@1u?nzw?$yI01{!2xk!f{q$ z0uI@?U2D9>ubbdJAteb{EynXylsni)2+X7K7S#-7Sx7YsSMIndexeC|5cx=lTYfA@ z0{cHIX!t7z$bTL-;UFp4?APIaOlOtH!n|t z2c+`%M|<<38^-dNWB!1gm9K)x_Lx9BB=HLtxCcDt0XIko`89A`?IbN)t{BK z3Udfe=Y!anS#C4}(ntM$c?%G1L4qxK{=Urhh9G@;FK7hm%P#;g4a2@n!+1%bZtw;V z)+62~&sX9@{=R%4@GA9X%psP}WWMhmn-0*I-$0yz|8RZToTrppjIN|geOYt@{=V#f zGqD~NAQou>k(%2feV7?AB(9k3o;h(1f3vb@$mPfk z>c9T6w|9#E>mPgHi~ifJx{3bNUFO`em$Je6#@<2fAq=EnTPhRd>Gkn%`1EUXt}guW zwQo_6# z{^=p;$>^SCyJzvB@3yK_=SlP>$CslzvAwCC{5*!PX= za_W+^>eklv*3U}LTH6@!n)@5t0lfE@j3BGZx*c(kx6Id>XUYiXIKi=seOZA1tHlGG zeyr>3W~F2b<+R)tzL8CRgB!PFT|h30BW>;lT1Oj-!+zJlpEfFl&o6#YrA|ZDm>JS^ zsrZ?LxY^gG*4p7_Jqbiz#}e3-0TQlX&X>sioOm(!x$q96z7DgR4i|(TWcIg7Df<{*3ifE&_b;MfDw#GvI^E@ZLPU-}$Sn@>V8` z`{7sRS1tqH%=|3Ez4sdMUW4&oL-NJkmEgNvv#`Fg`>k8Paf4c~AWg#p_s)*b_R7!Va(|FyU=mU`l zdFxp1@lLkK66pIE-thvYcj$mh`Vz$t`52jV=Ub}j$x;SAe608!IwZ&RFcs`GxerU} zOBAK5vA9dc-^jPv4ov>$bzX!^iBk?Eo&fg}kv9qm9`7tM`PK%PZ;|(z>loyDCg17@ zy&!z+0PrT=IM`MgFYBCfn!3+!#Ea|DJdm{e`4;)=dJn6VN8wwL%E`8-s6E4a{$?$viCo66o)=k{4-^)}eis`mN|WnK!rJvgoDT z!d;C%OEOjKvX@LIsnlm#ZUR!;+?y-yp3k~rY&u*Jyc^&*`z+TDNYCGAZ$VgDpM^hA zp9S6|4C6R&pGCaf7b75P_xIT?cyPNQ!4^EMSI7t5ZNl{T3@i=&{r9+LE@gd|Nr+~z zEiTEayGwJOFaJ^&e0+i0&j4A%%3u$}nP;LL_iw;ng;?9O_^!>ZId8v$lm6XRFE^%d z$gKH2y!Xeav(FYAGP|+IVFT|TS8~cVu$No+;T}`#4~|1#;rpnUms{_8_N|Y8_1U*D z?4Kh3zu<1vD-qA^Z``P43&eZkBfznN!#i3({f)`DZ@z2tPb6+WXS(JgU1r$*?S~7O z-*C8aLuP-zVZm6AG_L@iS>O1$M^|w8oOHmqa=JQ_tG@DY@4WPp?cWNy_O7Szw)U<( zapP~f_Rlol#7fF5lPk}j{Tb}!;d9Rbb9@${?yRdZT9fihiN`UuQMHFNCJoXXJ#|hh z@jb|m3YbTi@98<4$NJnU9I!54W^rj%j|J8Y59~nsunYyry9oY$pDExh(vQ@?@Ie2* z&*!F)TRw}oOdj|==mp_{+`ABj2ku3@zhIum>3HDs8OSXS zIx!v(jA74m=VBz*J%4Y*S+{)M&DN}FEHo!HFWMCPQ0U4?bLbaB3qrqy1p7ep#^9*+ zle6aIh{x4+Xv8XiKT;;`u})BvBO7}*ZRr~x85kWw_nDDM-+Aljuj?Bf9p1X}?$Ln} zGbQJDUrM>%Y=_iKe8Nbolr3$u;a}hxbrk{< zuPZ-q9KMUUhy@sno#k$`onC(S?>`oO=b|KJXdCAz6Wr@VJ>?$tJtf{BHkA5)v*ms> z19y*q2>z2yGx@ivgXDZrfIP*}LB20!Zngsr!r8R1(iugmU*?lzkGb!QZQ?5Zs`{rN z{fpHpGI69bpviMU7eBz0(p^^#-m>)o&e?3;T#U-)8v>NUc%E zH)ZlecuD#xacDTkb-_rVgLBHe#QbLlx#lPIgb$G37hz1k@+AC0#@z3gu(`*ONpHlvd@qJ~;_f>Q#yc_h z9ll#>U!r5=pF9@4C>7lm*&QAWL5=rlU3a6srm?B(6|9CFe(A-GT1H^I5#akcXRY7B z%a2B}jDipN;Ee{p!57{SdW2H{klvwlNS?+%QUd8!I;7J5Il}W;p5%F^j)?8m)~~96 z`lUQsmb^d5f(VVKCp=L3te$%|D~Ip8)*cDOyR9%hTg|+LQSj8u(y#a=-583HRDEn$%$l5h8Z0!n%6X`2pLLp~O{%m}I zK4ceg&g{0_yU>x~=8r2j`Z_cHE6pE)0|2n?y7K-meF|RHB z1aXF}NAs4mKO3?SpNPcwWJ5T^^1twX*+wUe^KEzJqS>)YoPYUd7~hYxvX5r}w!-}x zuJ|N$MZza%#24NDt%_9D zIN+sQlO}It|C7!RiarczDvz}NyomF^O1cl17Yz*bw|4|uYhE7wTa)`W)AV@J<=nM_ zy)fO@v&G5G6UBp{_!0UsWW-FWc+k$|Chb>qlb<-8`wr~yz_v5b77u1$&CSL9v>9tX z2mkqS?vMWLaP9?s+xrlnYdxxEZTrWwXHWlqot1>HHwn3u->2j`ITgn*{s=nd-Nl3P zmvWQwU(mjx}ZQsDqzF9Ej(I4B`Mz+@15XOeG$U4d(op z{Y+p39G`Xdu4ecaguh?zw1tReB_%`N=&J)LKXR^8f?4W&HtW3r>0vs6@+9e%Ag1~W z{lpBZ9#eW6QLWwd&sF+ThOlhO>pto}W|>mHH2Wv}poNQW*_=Irlm6>&Xj{G<^KSJB zLZ^0=-OI>4^LtOsOBkJs@zJmND4MSDQN~Ak)S=A|e?jtQDJdnH(@oTM8%IX28!4sw z1HgY-R(}4=@)Z!i%CaTDOSlh?gKx2j+2@EuLw`4nNpAvvwg&;Bmq36??{(0NBaHM+ z8FVfDq^nsLR+HW5uoMWr0r(iN0Pzyyfh~A_aGUX-M7%b_7_abi#y1ROjt?Cm9%Ovj z0z3zXeT(^FytGMM@&Knrd9Z^H7x0P#|MDEyImA%8Q+ z4b~}ml`4M4&$OFC_n=BtomcN(N0;~ql;4=;Dz(Cr&|+S;oV9$eZR*fkZ^9lhioW)AN!x*cl}=au`Y9I5!zR&->bfj z$I|aH(e^++)7^3NfV+O+dA;7R+g7#vKO(H+j-N|6=W|e+Y-%Erw!OWd{F8f#pcaf{IXC7`Y6ktQaM{44O>qq z40(j`RnFro@m0SpwA!F&^1*d3Jy;5c@W0Y^(~=gSgxln+>p{;(7}LjeN&5QWHu>te z;SVBPy#YKM8DpDcpCdiypK+S9)w{s?B*FyXRp95VJCVNM6eQS!2ia=rO!gZsZND+_ zK93~yy#SA|Qs;K&i~5^RT!*!W^gfSYp}s4L^AZ#Kj2h3gjbb0>Z0Nvv_DsG}61BN* z3m%+GO!`{#S^gUn!vPtFvm*n&iHU`{^eC5Ij8nOwpS7onrm(;yvfJVvO%& zJ>XZY+vcO*)i+Q=&QFRG2E9J+-tNqX;(N2<_^!Mc57Ja`J@h8lhw3f+cd%Z7wPVl< zIj^3uLXW=*dIiv!Z#kJF>4xl7p3kVq6WSr?r4#=X^nZnC>1?JaQw%wObt2^CRs4** z6koc)b>%a^{r!_CC!t4-{|VWgF`L)>3ukEwk%)1HiO+1WrVJ!o=k%bSv8SQV37Y()(%TnfdugFF!Biy+|+aFbA!_hMV+1?4BjWEyaZ& zl+n!=UW8w@8vOAbkd<7UXIr@j@mS7ZDY`b@pviV4ZG-v5*sy@*!8nG7Q1@srJdYz` zRw9{4f5JWNXcO`OT(sJDWurFAXyL9mS%%0%ebgDtGc0P4onRR21ZN;m2@Rg{QS|~{ ztUoXBxM^pfHFdH}Btx*No9}9D9iOu&_Q321XYH+fD7r85FwXLHH#*_R>l<6U<{IOx zWxarRy38{_*fbDcU5{-8b8AjkV7^PB_tLf^zZ!&dY9D2Va{O6pzuG}T_c*%X$IAAz zk;}?j11Sg5-J-24U>#7l_+{5mi@eqG`Axpc=Uu+%pZEFAJ?+Hr_dUPM=fB72|1F>Y z0oTtqp!Y7gvWq9p^bHQ~*rfEyOIJ{z?20z{&<4M=fm5T9bbxUeKfN(HfODicR@(21 zKikyH6oeESzO+rmZ3Ejj4Sk*8uQeVI4{zUL;kB*9J3vF^5xj~q3Z?~v4a=?t71BX_ zzTW@Pf`Pc#^|M}Euvw!u1RIxO?xhWXnl?7BWoRow3N|*-HlvP+$MzFO$6iDmgDYhP z#;4*zd~4`OJQK8mpQerX86V%nBc&f#r0cY$k5|AEFerL)?Q4EMf2xEUI4h@UG8^)y93tBHAOhD)*5Ma_9 z1wE#P^x*X9eHm`j75S(c4xxcy&pik?;}sy@7V*HAcNk{I`y3*(5yp5KPCUl1g&~6|1e5JJeWxOLu-$NDfyt(slY~$4h)1Irj zQMPylxHl-J3(}MExTpNg{ppkFiG`5$ONGWW=@9qEownEbS=dSi*8ii0lOG^?BKmB6vj1Zi&z5lFZ6o2l^p z{c9>b$E9b+tIE}lTxy8ThFJPYQl;UVZalCY8O4(G`t z^#YsV4qLYuP@cF$|90GMoqhsy`>}6*>WSP=%xC+Z1K9gGzkqsEx{8=PzjfjV@cvrt zb+qae&Uijzt;N`C)ys3xDLBq}@!ia8&@-e9DXXQ>41W~nGobtOE5{0v4DQxOoQE<; zatOoR_mTW%HuMVMUqXl9-L|YR6?g~lA?rx)mw{(lUoK#r^zuNxi{)TW-8){a<33Hi z6Nfwe$+7wPUN`O~y1fu`#v+kL=^Xr_m@_|rHQY_f@nXywuR}2+@AC>)a$p|x?DLzG z;~$E}VUpu@{Ym6M`5}C#4Esc>$7q;WK>Mn%lX>{q_fIm7;fDEzKf+vny>&q8HonWb zc=8#fr=%zP7~Ugp${ADN5tlmn3%n=!J%;zh{UqFRl(YC*mXnHZy1;q~XZAzJna}UN z6;`1R$n#m2NIP^jjqQ7G(jq?dO`9ixrO z2-o2FE#L#Rq2N128wGdNwRskn_?(+_F205~@K{IOtJVZMoLf^a=DNs?B;e8+2)B@` zH;C8L7|ymSAT4?c0_MOZ+y_}aSnq}rhlXQ#7YwSvgXci2+w+xVegB!U4%CH=vE~^5 zMZ621GbALqKP<=?&UrKUeQ^w@VfutV9(k}D5B{^~E2Z5(hOb8YDxDdF41a^%h8aYL zzu3E6!dHay;ZO#=J@vl)ZWX$s0j!l0RNB9fE~|ibM}BFIY@XsnbDMSF#zgpn`nKA~ zFW;Hl{DD}{WS?bUWPc)1j%PUvNa-gKVD{N=_-z;g!Q&j(_rjr7>>$x4Yw_vc~E zKHCp}kUmSfI}O7)Xh^S8pKV9H=3GwF?(eg`c<}3>As~3@vmI^htS(F^cVFyxB;r`_ zVxNsz$sBTc7<#53QSWs2?EUAxi-~!xwK+J3U<33X2=+G((o(dqQh$c}T>mq?7aaW$wTHDrgBv9>>oxvc{{a` z;GqWSy%{^b5NVYaAL=`ERb?EPYVN-T<;A{&F-rmG2Py4T z|5Eb)v%UxMbWsJo6$mr=26Zu&_{L{}XYvj5gCKmP0rZ0K4eEqUy&CgK@be8U`c^#a z3%=2QY32vzGQ)Io0p-84cZE+#IbfX_AVBa%A@Pe+zRqD$uLjfsye_u`l!4@Rgg6HqcQ_TGSP?qzi+Pp{_o3|p^i#Wow>g5ab9P3zcJ_P?jT^2 zSIk&1@7}>t?cQ8V<8r0G{3(=`v~$+eRDJoAcxH~vQ}yLFz%$3?_o6SG`H-~x$7S~A z4a|@Wf``6*OkI2IUW| zjBWM-8E^jsj?3N`AUQn$?;Dqu4*q@Jm*1OlnLVy-TvlC>I)n>eFZagf>h|WJkIU7U zu}z*N<8AP9d3oFN8OYdeZPN9p*Rvh->DjoCm3LP0`*ZR7*xR9m-b0nxH~n|#;`PmM z^Sg7rtBP*!CF40M(xE*ajkkT@n=|QAhc(q+rvrG_)fgMc4kui#SC@>|t&MD`SD%=R zb!KV=I4$L2P>Uz^*^%Te*U zxy{D+=17y@o1@&yd!g64{`+X7LKN@1(ER}3ULTdB4}>h@pj_CuO?@BhN2N{lQMNJZZ2&#i9mALwq4x#2&G`rQeL-{yD}i?$ z?{b}yG)YhBi+%+`Rs-U_jt&!%QD6Ol zF#Ej_X>~fFl8%w}O<>+c$Ed1QE@Gf(b2Zi)HK8i17i&tt$nq1AQeTMx=`VctG>lD$ z3xd}HzsVD(T4VeS@Jya?2>u{E;WY3j-8je>7;hz>@H7%DYtcaQ((dO8Tx0wS5nPZw zdOQIlZjke97rXz@vYu7_*0M6^-7~;GTx9#h|0BJ;~(CN;=Ic(_nvv1 za>C|&C;vq4C4d~jZ$fa)58eBBZ~eY28%P-Giu;?8JB02(;VyB~ff)VrhpmwUWc)u! zsr^lBb9p?w5^-LMII)@W)cw&+fqN6i=B9E&>vH6S`H?i>9S`ppaGwQnx#wXv@C1i< zaiGrDy!@}9`AwLg^mhH;WiXE*Y#H}CSlBPc@4Hp{w%a19Y}gx6;h1-YyXzaRuDQ6F z0{`0GuiUl9A0g8jpx0Zt*Hwe`LhY->A7yVP^DX>Q&zd|IDU+xw@JJz5J?W;F^g1j< z0V#R^-pUE!P~H&`JpbOxqmb<@@yXBOMUzh+1HB-8@?GEs;gjD+yev}z$&#N>z7D*v z3KDF=b7h;YcW&=fUtpL{N4spy-o_})ht)1!+Uey2#*)R zdvN|Qk-rc32k|^xH2zJ+JM}jg(gl6@cvjhOES)HlUm*_|LS&ES*;60EdBh>!*#p|3 zw~u%soK+)kxR>|v;M`v5i8t%|#@^(27tDKr=%;>2rDy4Zx}QQ1#QT95?$HIUt4Q~1 z&_x{NXSGX=^@THFPE)9l-!C z(=&i&9aa)y=`nSN<@nV8lz?~Omp|7`LNIs-WBK!PUj(I99%7ard2OTbd80aSN#3lX zQ5?#KUd4wWSNNqiReF;zL@?@>$3IWu+lm_hWT&DIh+q^h$8YkbrxX+4h?Hqm>2J0^ zrb(%Prp9soY)ibCX}RlXo1TaA<(>fo+c8s1SzUnd-zpy1^vj-_18~nn`QGV1XUgWG zJh!=iTEvHNra;FZSNG2_fNgmmeh;XpdX6>^`L{;&@S^5$JzKYL9mRQ34mdr-*A4Xz zk6!2S+LE5qg-~TExC^RpD-V%cJ$DX_4sGqH>#nVXcv~Gjy>oQ9Z`%+&3a#uT24-9a zE|bSr(*k8R^LP~oiv<0!S%xiF>+j%Q1tGrI4JZCp3w8d(s1TOJRkk}1g};S$JJq>cZ}wDytc(JS83Kb-=Rc`hcB&AQf(gNxd5HvtdYm zmV$N7mi(HSNy>=V4ED(BzD_iY=nuDjr?ZZ4UAj%_6bGYf{$4%4k%`Y-Cb z*|y9%{2{!6CZ_h-5r>BUB#cRKC;ZJY0zz*M0!(_m=Sk=>EkbWA+$OyV_}9Y-2)zLW znDmYr^hj6e{U^9hdMDw35=KDieH{TNz1KhwT|(uTyjSQkJtn;rJO>aaAoTVlz@&G= zNFTbQNALIHHt98mbwBsh`=*cHX(N3cjU;{lmyaILCRM7B@A~MyYtUo9h2Bvgy#(mB zB1}N?djtVyejC8Yr2IIV3B7-S+stqO6#4yIA3gF_q1R;4`>9J0mIB@pn|0l^Bz}_v<@l(4 z11fzRl5W)M?7vd` zEUN^JfV3Jm^)eoX{>VCH8zK%3i?|EMq&Mn5gQY;|Q4eL(#gP#502qY}>ssxqx{0LuT-pJQXeWRj7d7RZDi*&pXNQIQm##DRY9lE4*!TSxp zJv*gHs>fyCf8S?Gq0O@YvY)XgY3T2Ukv;>b(gE0XxFEHjfZyZ`d-0O=Ta(YM1)j+l zw!j~RFFXmn3=HLPk;R3+DO+)!@#DY|khJ^x!jpK=CrGdb59@u)1C^@bwWM9K-sFn(%)tu#k${pxVJ9+Vz#gn=YembR&+K4f-&UMvJ35 zzG>qba^sP2#j^+*m!Fp{GRl^^2k27yy?A^E{?SNRV=B~(Prtv?7{lk^8_T{mIjC^m zZ^c8g`e?Kp*c;0}7GG<8(d66gIo#X0-pjxA5FJ3iSdOpni|_aFMM4mK_QDcSuPK^A zmMi%v=Nba+6H8nV%hJ^AG1%p^L&ICQj~ZLJN4Dw^>cU3u*PFNe0qak`E=Yno5~kk`GzG0#Vn&x;6RiX+xeP4h>t( zr(wv`q%}>z&GsN5^tupW()%gsv5ioksgwuK#rV_m`A>5=F1<$h)MtV$_ z(Ax}m**E~=fyM#giF}BuiK55!GEQ?GcpmxaM3?|xQ{W#5TJh37f&^ReL_WmIHI6z2 zm}B427I;0=)`ed-4v<&xx*qG3m}_$^IJof7ad#>BGYr?y)?oi;3cTCOW()U$S0@tS z>lhOjB_1jMQSQ&x+Ft|4iWV4t+lylc?usaMWF9H5fguf!5pk>yn!KA?=NPo>Q#x+W z?Kw_h?GE>U>hb4b+n?+2sem5GD5inq6XO^9{xon6kZEW_I+;es&#{B?%NV8OE|-BF z2D)$`Yz!E?A{;ro`SFlPqg{=ALxcEg$SX%MfE<45MNUiECj|66YOGPqa*swhehhN_ zIN}aB?+dwlR5GMmxaUC|KUbf?GT?kogY=4Lma+f`>WDjWfBotnJ37 z?qP!I=Op`eWtNRa+~*N>t}Q<7q`%AYNa=f>)b}_pL9_l{bh|&^jq!rx0>=oBOPr^m z`z^3C*jr;`9D*VL=UByY;*Va`V@SvvDv&Pjl?FXM9z{kulMvF;#y2^w?n7&P;t#hiXOo?+7g1*~D?OVLb9^_|q8mw$yWy zb)+%cb(p(O)RfEhmFf0g!@KVM?9<$a@@Rx_57wK5X4hOb2dmjJl>M&+x>1XtV`lmD zSxUw|E(jiH(gBsmOe}>fU^#Qlw6$0Lqd$0Og;Ds-n{hVtvKi>B|4gGwQ;qb4cjWwkU8}oY1zy#qJfhk(#qg#eS@8=%*WF#ovsRfNfyKpNZwx}Odg zgdWF7lip83&qf&Ond9CT`0;26mV)(|GhHhl*b*0QJn~>KJqRQ}qG7zuKjQ>F54axM z?fFXd{Nr8|2<)tYhjFiC1(k3vOsk8#G5DQ)dv#&`eBAr_xX0P$`*z%8|0{uR)Z(8? z*LYkIJkF#8Dvf)hYdiv!O1j2RxU^*4Yxihto`7-h99^Ru;*NTr;!!YbYs~YKXAXHf zuzDk2ZLJrvt_7q;EkS@erW^%tuQcXS7r^mix-qZINAGpet2E~M^^Q}Gd49d)8PKy4 zhkwl5j4(WUPsh9j;t*h25d33a90glR?|3~v5pkmoc(A7(^PJ^@^^OTWh*Y~bO)FJfyvnQtsZoZRyy^vXLe>w%h(kj; z_%krlKjE}`VH`SK5PCPmZ_+zz&||9+dfY=}@|FqsgUHhv;L&hhThb`$1w9W^h&RZd zm)C(uJ|`e~bk{W8@i@SmMtkrxS_gDOJ>K&3we1ha+BV0}66n2->%9Q!Av$2HwTXTY z|6CsHJ%WR4+Z-ne8(_$D%`t|3;+Odl0Jf?0EN#lC|M=V*ahtcSJ@mK%=SZ8b3}>!B z;Pnm|t|N52Hq-uIu50U5AaRCx>87k|9J`kEDXdokX+3^^eG+mC`7_%dacJl#A0{u8 zRz&{Fb|4`1IuT&<>ocIown2J)SLpfI5#GhKO7#5e2xRg$a7mBp5_LLc-z zI1N0m{R&9h{d{d5(l;PTum#VRYlr)`_qTTh*{icOIA43m$Je-HlRRw^_Twzd!r0Fi z7p_j{=Hh!U-}%K%?%?M#xygUdy zFT&Xw=ZPZc-X1ON)jar#!@0RwsNyA>93bi)GSkKslIma?TwE z5^%^Fb~Ahp!dEv&aaz6hcpb;o5@>m|-3ySGs{?qiNjZKUP8pl$7C3n>LB!(XR|z6$ zl_GB@5NyhuETeg#-&#UfeZv}Yn^{S|)BzCT+(%{lOO8d}nj8`)M3@rOkOIU$ns(P6j_gLAORyFEuj+uCf+FgtUCe0^WQ7ki+;^wCvRda zAs+pd^+-=314lwUu!Y_i;Wl~a7SOX1MtW>zLa!HYlXsGM5Z=jsCB$X>z6^%+D)CMS z@v;pG@HK*;cXG{cNIz2lg6Hy1+2%OCzVU`OcYFNwdrMk$VU_6we!kB!d|zY3Wo31C zA8O=3!ahg#jS}elKiFS2NYBzf%C+VAdJo4=LH9U=;IsFZfVu?s7Xr(dy!}e$(|>$6 z)xLRR@Huq>0`#wPJ(?ak(}-`Iqw<#Ta-4RPqac8G3k?RaUfK})SvHq&kE6~_X>7r+ zO>Tx3T}4%nW7d?+&3YA(*2r(vcvDSSoRY|?sfbhi@L&=Jyyp(h?0fH zix=A?qkWt1vPZV{jcnPv{Z6}oV02*9=s>@XXE)iS1H;?4ZtsI%u_?;&t1%yFhpyvt z>rYNzmf_u5-#$sVw*TwN+3;V6J%pBXOznrijA!Yg*9cqb0`(ZPT>F04W?DGcsHEd) zkc7e}-)J)Y>*0pZ<5BDd>@dUG9%MdHYdVhkM*7)mKMX(ng$C)3+Q+eh&r2Y^PKR*q zuKc-wY?$M%?ddn>DExIELH4K;M9`bc)5qRbf(RtE^7tsRm4J6inZUcEaab8Qig{Z?KHOop<*Q_cJN1TkhK@ zeC$J7hHb$Boq)du51GFf6jG8lF4SOtxQCJHZ$V+RUGsh-MF;)6)%7o;jn1*s^(Sak zfi{!*m3KB6IUgRqd+0i|3{;`0QBhqM)Hgib_jSy-cJ%j+_LU)WarZhDR@-&$hIZd1 zrIvq<0oGU2Zuwoeab%>7ur5UPUR4I+OEt%HP%Y&D(tF$CW`7W1T@(Cc@Ij0rY}af) z#G#>|b2s)hJXXNABQzKwxc<4@A%mVd2KV3{rf(@<2Cxm9Vyr6k`4f-v6=3ja2$llgPivN+#6=s^QxEzPK)Hi@ zY_JAJFAke=N*>^}+Tr2dP69%Y=`itHk)VGQB-n!I&V5JwMtO@)ki9m+?{=mx72wGn z+-oz0vHW80(czeH>iWq01QKwVcj(mG_|Cd3K@?h4Q%@%?OJh`8(;y2cBw#Ad<*wpG z7uZ%fzM)!6S~_^u5-@D{#v9898rLV(Sb+qoF$E1*#*_;iGswgl)d!P5Cs1YrQfq$x z%t_#cKFZ0nWI9K3(!as|@>aD9L zode%G37m9OckQ0()bG8E_rkrnW4PgpLSzVYlcc); zctL2vi-kk+Be~fQX}^}6g*glF`#saC?-^!T80#NA%fz|LyYT-6cYgWPgLLG0b_?l< zgl-y}pR~q5gtVyj5jV}}+!Mw=Dd4~%^B3|D0k*A0;(^Vv7wf#^v-Z?I5PdMR7x(gR zsDI_?pUUmKCQY`#sc6hukelSaokEJk?Msx_*KFWFUxoaWi&&z$B1C@uII|nMo z&3<{g;_(b)y^&{|<<9;lvL5S?XQn<5o51>;hxGD20phO^4{Z9$PYA3-&Y3=|e45wH zLwYF>3J@P-0=uFb6Cj4`Q^x$~z+fCa?itZ@n;vEH;o!)?z+D=PE@E#Q+%Ym>^(YHZ z*dALTuy8E73PQ^o`Y}&Y;jr0VuflvrjWHO^WQ<|jIX_`ux#v@jMerx+M}co~pAXVT zk*8e$IBiVhE^v%;bAO%t)kW{nb=hP@mqJ;P%6(agE{CcOcC|DYVVwwQc6;tzWD)}g z+b`P}acJo07{+lxdSSmC4oiX1V;eQ`SkG+#0)oe}j{Ps;K6nA~GEN$Ey=gQ2CcPu@ zvyBM|y^RPk>2W{5rLcif<7&P5&u9_b0Z1IK>xz!rKB z!fn#y`G6$Cm|r$~N#B!j+zdL?D9;}0BgXb%y-7f<8tid7npWGf5#1h^_pp2Se^`U1#-}2P3Uhv*%QS$+~AT~>NO8p$8*T8 zI%j>zIi7n{aV-0y5{&U@7_GJ^~gBht5V zP(9^XCTUM_Xc?WG2UAOCBO&dkC&h@XSje_iV{aa{b zJRf!aJ86@7%tRmQj?!LkW!U!e>;*9Ao`?BVdG~YoLzZ#9ITu$+HCSt3Euoi6$5U!P|xalincwmGG!H zH1SgKS4!W650Cyzc;9mIU@1uBKc!syPty0h@S0=rpgYh~$7hb|-+|v8gKZ>M`Z_5x z?F8Ck<d}>2UW$Q^wVCKx1-}X1CP9Y=DgRX z^a16GEI$D$b>WE;{u~^O!~ZqI z@LaeTc0!+Tn1wSC$lu~RYh4cIw-5IeWh2&?a^SZ&G0f`S69>5ku(MI?%L*riZ=LZh z1$A=nK7t&-zg2190Q-Qz7C35o&4A-f38cm8kV$S-`4&%$;p%P@Z5mfvVZ*va-J_EAAz3gP_-_*yjk#EH7hquIvHu^di> z<_f@z;;uNAF7-Th0^eEO_jg!xm`Wxx`Q8i2Kuoe`6iAEK zzDjbD@ImHNO8s-+U791>rA8ftP_HZ;><#eN(2{i+Gm-M?li< z=Pz&I!GIva7Ci8mWiwhMae{wyV*{84z>{e>_o7~A1LOm&B9S(Gj0m)i>e;_6{+e48FTEBnvT*Tf;e zgz*k-=C1)}D&5Q=p2^;WT3gHT&%IceTVOOu3)H?!{7LvO^GN>m5%*16Y(tz*ZR#5t z*tBiP+oV+r!n*qC*M|nMsfKYl9YC z3n=^gI?Q82*8i*SS)t-){h6lc%((sPx}zSiZeaHK+Gg z{ohNov??7?iI-rKsepN_Bm z8uU!QvJQAAU->rtLHNoWz?+02PdT4Fvmfy;295x{3jBQKbv)=7B-nxnzH;MC?r>~h z8vMR~_b8FaSK?Sb3qh}P<`#VG%d(ydrP77|PJG)7d?uC!ADO_u%-(DPP5acW)EBai z6W}XD>B8*nL)nmZM-E%l7B^;&zeyQLm?89{hgC91Cem?=Hf9 zGG^VLn?3Q66&iYQ9?B^4J5=~ z)r&k$s&joxf7N6^l8+>`Zocz6d=m$EAw})o_@2Nc#c1N2Mb3Ar`@$}Q!WpLD>CV4< z+p`GY3*Ff2d<@@D2xWd!Y;gXnOirbY8gjC^&R6(-xUp<3vp*kRxIY_l@E+#NR(rU3 z26TCUQWWhY2MncwFA=-Uhb1!`A~N5<>&_#w2KlGxd__Mphq4+nT!$f$-2+!=o@w7W~Zz29Bl;IyB-Z1K$bw=3$<(_Lu zJyK-3M^f;k+`&VUw%ZFR%zECN#rA^o-xGFLrC1+$4|9|A)QPhW>7%YT2^wi+LAzg~ zOfiFcfqr#C6lsp2Pk>Gvzr8WrN-v~t2InspH?WO43G}Up&<0VTXcM9IM${A9jkO+o z&M<$P+=n*(Zzwajyxg{6tMWR2LWQ%uB-|}8))(VuUPI0%Rc~{V_YBI9Z99xKg`BSZ z^-M<`e(<-(>;u_wdRLb1g=r2stMcG?^`ZE^#n9wfzEWJF_;2a7vHT?s)-I6Ppy~{~ z0e#~3913$^(-6|bGC(?J53R~RgS5;>8-#xQ)Kq=$YLwwzl#6BGrPl1&XIOW=cxMj6 z?_ho=pDCVzjdD}G#d3}pTfke`H^7&2KS3VOpwC(<#b*l89i56~P@V~-*`2G}*|D%{ z`x%6J^V)Zcc4oI~f7WDrerR$o@^xDQd8y~SYM()t{r1)ZcsuyfR|^;`7tBs{qwl8j z9|3;~rMuM~ZjeEridy`(3(g4G;InbfS6Za+*sm5zhwYsGO2WwtqrgSGQGAHy6pBBH z^3ZKx!r4|ga$TYKmV6X>Z5TqINMS9Y_knCZ-kpPTh_P>={KlV%hzhi&8n)zY-wN~dNRtjx*cUHIORp^_$jduLy9M+lZBNOLq$7ggK z${20PXva+R9PnE3pWU(4SZYpcZwmD>hWbXCrgP*)Ok>EoC4aA*E{E96=G@6u*}q|0(z;F0!5G1G&*2!6x+Oo@ z$2FR<{M;n!1^BENjuon2(r6o?RhQU@y2bc_IvK|Q7?0Z=R2RtN3wg6--p3R zAQw#D>G2Tu-$?plmxugV^H%AnDC1E4XDEAwBQ5^^OCG|!)vL1Ky1Onpma0$gNlCfC zio8NDC_q=CcuigUp=^B`JVe6(B_Hp}{i`<&$%#&gIw)_U(X&QwakeYxfD#}q#Af7gJ~-2m>xH6eo9{*)0A zhx-o|G7{m!240|WisJ?rY?@cQ;mT4iT(7sM9ac&3$juoxxFFbn~l<uM!^#Nn{)X? z4O$n#Y9I~`TmD)YzAnAyAUy0>0^kv*%G(hR}`gkA~(CcPyfB=p#$h2CRuLyl*YRB!|juN4n$iHkOx zy9Yl7UM~X4OVIQcy*O;f33?u^Lp}yPUy0ro2s80EAbr1E0Z;aqKu>8{cBVYZyUl>Ll;SZf~ z}z4lgyGWv!B$` zThygM)9$aG!P*VaF#$aJhi8S(#;?dRQRufG%|`A0*(mfbjg*UUjwtMmM9Ub?v~c_52eq~&(E{9q|SDJt-vh1!Xih$|p?{lK-*2Fka!YnzbMR!1G3?e9t@*{508RY#D~veL#!1b+X-2X5zff zo5z7+U;I2y$5uslP*b`s)`Ni5o?mu)5*@>00l_QDPT15Lw>LZyY`LpfW!u}W2 zxn;+oLvkKF(O~a}Tr`%8Cax`p5?DL$9?wNr!(_TZ@77#6b1XC-X$gT&lyrCwJKT`Y zUERt#BG%9wk_)eCPlqtitH)lLyxp)V8%;cOLS!Mv%e@EON6dW=pt+DVB9^|3#j(_V zYo&OR-bgZ(S7}H3!q{iSHAd9!zO(CIDqIu&l1dxWg>_SF!T8}8$agA#3&P2dW+M$> z(()hZ{43^`NL%g(%uUZ8d85zCm0Cs+}Llv618MHCkiY`#Cs3t_tF@$J?=#pSmR zxpEzZ48_k$YNl%)Rk=yHvMk5+3JA|zhyW&2;S%+Gf%zQ>73_8bXuw$#*3Wa9X^( z7J4HOgr1+*CV}^P537{N60hAcI0$At-8^+gyPV*fUT?;`cL8NG8f39^?q+1)5@5O2 znvTjWUtPPBTGj&N?#^P3!D$ax8@p_;}<#{Q}reRe6#miQt zUp>jHmUL1qV*zpd`{sJIL-r$)S%^n}7mV~5IMq6!O@|9Y?^oeB<*Hu1Bz=*!FZ8|$ zx7pX%!yiPh+6%l%nCaxI4#bQ7aUMw8{e7MC7mmDmAb6OMEptEeZWE^M>vBxNp1!ZY zt|NKh_w~!XQlmp!fDWwG*HwRZF)Pipq_3L?3eX#|y`TM_Bd&(3_xEZZAV3n|Z+(6P z+GC|Y-;KOTe_+XXz}R%SAT^$V-|X}Kc&SpKUkkkVy3a3k(?(0u?(g%Xc+gh?uQa{~ zytAq;_?=Y;w7}x^dFr7j z=3L*ZvsYy!_Fv}qMCM^`wO`GbAQvo%Ch**NR);-+O4fJwpNM++k42-2{n3BJ+=%-~ zL(Z`iXQ6N2g!#+=f{vGSs%TQrt3rqq^@VvA=2Z5v6VyRdCmcrjU*Y}l*so?eZ}^UV zENdrRIoBtQCp4fbcbUA;Z>|*MZOj+g(p4FNx!!f1=(%N-MC4I2)L*~WP2cs!@ zq6=2_FG^1Fbip80JrU)S@hi$6?4u$TQoqZx6Oa=3>w;VHk}XKE1<$Vw=6x>YL*#Ae z(*;w1A4K*b-$;WNd7a2dq)#{ci3d%H*VL8bH3fb?)B(IHbip$_BiSaR%Jj0wgpUuM z?_R7B?zox--3;3GKHh_ca>l(7`fe zf4~3#xqBD*D(gGX|2ZcYVod4bqNYsL2Zfd@>Orug>(s+Vi!Id%XsI%iKmw$MB!@GW{@_&Dx z=leT(avCI7{!`lDE5Cfs@Am!Ozt8t~A5$nP&#U8i>^`i|RKED5A2KsARpW?rjUO;I z5mTxg;~yxk>+j2jowD=3{8r-}&inLQ8IP z%F&yKMVy}2XdEwzkCW!$kABtW!tdk9V0e}G675HyX`H0B`|BNX(51bYSLxmJh5`Kb z$xO@JS_U&G;oTz>>lh>_%hx5YoGrCj4%dk%<(AqcWWc=(fWSU`=*C@)6 zeL~t#G&_`GsyKfT?knL5!MWB8X z=LM<@r(6xH-^cs-W&){9U4N86_4g@g(Q|4BKYV8Z9X*YS;`C0petkRs;yhys=E3j` z@v8l3wNGiq>(U$N8B0j)K=g`v#%}!G2MwoO9+_u-cW-s1Cl7l4x~@omD2#P7T2n^X zM*Ce7b&PZ1BZd7hnOf~Nt}OQ%%V4iPF>=!$y<+|nmklyxE;g1q=Xj0M-ltqkifjhr zEu3)Ie#)dBQm(@4(TrEt;1~C>+duDuo zYtPIiq2oL6d3~oZ31^Y@oKw6fLwPynYPgv99H8^5-4^qn>Dak`DH}l&%U}`b zJx2-Tctd>bawq=cyeEr!AYP$3qpr#i)PB?AJ1&imew_DY2{=IkOu4id^PZD9)H_e0 z^9S#_VWJjyOr2?s=OdHf#hoz`Vr=UN)s63TCX`HF>0{6$U8jecvp z-_X<^z6t$6%#DYl7d9?m?%meH!{_ZQ+Vl?k@&}f8G%oWwNZRb(cFi3=4y|!|4UL~| zS{PESZ)j*&Xuc`!i~Z)NomsI`4reMf=D?AC=#8$0~^W<5~y zfWNS{r6qiP9q-=M`i3a2Ei0BSAP1y>;gY6iqG<7^H!kNvm+(BRZ?XFAjrXr0OAQK( z-({`sjlSilQE5_HG`6&^SiHnv-d4Y`QT}T6y!-Oy9E;`M_m-wGu;O(&nO?{n8Eak9 zPCgf`SpI-_tICkqu$Ol%_olVCE?<6?l|pk<%hD+Ru&g87DDL`LiO*=IY?_w)4OEWO zF)hlt(&5UYsYR(@92Mwo%l&w8>gTid%}u1UeesHAjVvJ8^4q8g zTF5k|x~OR}5>`XMcowv_t9lna;I~8}p)rT*T2|lkfZxhHJ7MWBhn_V7+{1h~G%n|~ zY#T?4eNwTaF{)&e(ioO;J=Ij-&XMCr+E`eA(B(Css4~Oii|B-HBMj!b@>tPALm;@G z^HF)l&xiTC}2NVR(d^IF_$y zw$>fxAZl~8{`$rA1wu70S7Y>b!69j+b#*i?YNANm8<(|yRt?Rid%*)8jU@j56^$)n zqnu9q`9`T3_0{QeKVCE{o6w8ebiL2Vt+9TY{Lc=5`Aw}0JDUBDre%%fA*4fDMoz9B zFZb?l;?Z-;ZDCXU!WB(nb6nio(&*LlSbQsc9$Y8At7T~m%~Y*1v=_ZklKZA5t!;jN z2fRUSBI>Wpq5|-4wUP?kAL4y;qgrX$y}ap;?}UyDZT*S%)J8q{E%nQ+O@)l3&?c8Z zuxvqVbJIdNiz<*Z;9aOi6lv5<>Oad%>|x6Z>p~B;ds8BgDdQN9tsqIa}M%!g*#(%N#Bb1#&gVu~dw*1y{u7p+*XlIUl* z94@HzG={9YknR{WAP{`j@>4{fbS>)mTs;hLVZDeuMjG-H^>fvkrM-_S7_05Gcd@}R z%UyO6{Q;VK~&Y&lw(`fk{2m0u0bP8R8_RuGA zJBH>`UN1l&MC%@jy=aB&M(;-NL~lnI$0>8R$bK_stzE50ccNFKY5l!P#%G!bE@AGM zbDjy!;cC3L{#xdS?Cvei_pVBA4bOwFH0&^JGi)|oV%T67L*KB*u-b5>Val+|u)?s^&@()li1L5J@VMbI!=r|W4G$Rx zhOZeOG(2Fq-*BJdUc)_x-G7*03T zJh0qn8mf=TJjZaZ;XK1S!v;gm`^tZ_VVj`@G}*5-TxHm4xXy6B;ReGl!%c>p4YwF( z4YwI?H{4;k({PvJZo_8`yAAgk?ls(JxZm)A;X%XK3gqVcJmqQ0|irZ!*jnYHyOkT&;b{TFm+-$hTFl)HYaJ%6S!<~k^ z40jtoW7uuD$8fLVKEwTn2MiAyzGfI09x^;^c+~Ki;c>$gh9?a@_@T;8`#gjdhE<08 zR?A*{y@b_<+Q%WYZ>V!PGLJJHZ>aC2>?avcHoVC&V>sP#hG7F(!v(zhRwwzspET4r zU*=N73d1VHl;KFjYQq{s-*AlKIK%OVk}E`ilHp{-n+!9C(+y`B&NP&4qVRJJ=Nis4 ztTSvdTw>U4*k;&axYBTyVW;6b!}W$647&_B8E!V*Vwg4DX1LvOhv81cU52|2pE2w< z++(=caG&9R!vlr~4PP@13=bI|Hau#0%<#D33B!|y9^b~W{tYV(C0oi~cd`jb8de+D z82W~iL*;Lr;dsNe;UvS!h9lvZ7xS-Le3QlJfy}x$FmzXYmp{ytg;mjcU72UdEJ5L2 zttYo?O5*Cm$9jJI)qJ8STsg*l{_)YIoxt1c8Qn@E6f2Uv@ z9<0v$gdx=#s>%3~oaL1KNqr0*U-OXwRDs{2*wb7zl@hLB*Mp+#NOZg}l8LYim816u znBw#*XgiP$=_#3xUI)M9^tw(%udRq)b&Q_karC}u^ng(9fL^ULfG&Lx@jITranMU+ zr~I-kS)lhdelsLe^r1Qly{nu7RNAG4uHEV0{9QBgh6xkg0vC9uHLeL02LF!S_9#PzHLfa3W;%OvbeB#t!}@w1+IQo% zC&ICt=4QNFx29g3qX^hX9IdTut-V~oZSSzM1B>W4cJpOvO7%9q@1B{uXzTE~H$GFF z=~JBKz%a95;aXf|G0-|ZpZicLzgmY^dvQu_#&-r#<_{H_uP!nd#y{{&w+|@JCSi`+ zSq$jG%KjUId}wK1ew>iX5#PR;Hn03}$$vB+sxW>p#_+EAd1Ld{Zhw#)j-ADY`ri~U zPE!oD^8!y4FIHVTrdbglOV%5KX5Xy(w74=}Fd9`;eXZ zv!geQ-*Fzc8}ndz*kSw~Lu;L0TJ#+KI1iI-u04)UxwIGaFv+?LoF~xvgNIFgH$3cy zYX^T{p*K2=u)xDc(8ETiyYi{aw&pdzywDGx)_q*u=M_+AqfUoDKMISOG3Yaq-` z9GO8z=e`H6@J|0abX?dtzFRs9t1xk7kCo1gN?e&V07k5gr)veEq%>I2eeN{{pB(D^SmJNwgNv43Y~W;D!8Gs4M-=Y8e5 z+TE9wjhpwCWaZyI9X6n`%g3g#zM{bc1>t?=>O*6%z7`V*==U{+f7msz@IHOj*kxCZ zZSbzXVg>FKBft2&(E^TbZtibq4r8xOPi$_$6V7@Ihi_`{JiS-3Gy3k9bny;!?MA-2 zFNf`TtT%ha{j^`z#cH~1BK30qO4i}h-kl>7yt{)=BvUV!d2QJd6VkjZomn$HnOT#p zz`xg-UE9o?2)E#WF=6ij8^N*oyUXh8ZrX7jWux)-zm~3m88)CxVLp4&ig!IYhVRoB z+RRRLJS}^%cjm+9wu&@wL+?bthVF*mTpDW^dK>ycEQ|~5uz~MiCyZ_`-@iG~la^ax z_WIq;Z|OlFG2c{51A!rHEGD<0aCeAdstA|6}X_JrQV zDcoiDn&J;`?HTIbmD_ph)D?-;Ej?x8$*C^|N^gnxu^{_zc}IgbTf)0Qf26eg2lBPL zH^|@rI@K|1NcYRH7v6R8*CbE*N#1>0OPTYAh@T7Jcq*yQ=AI>8wWKLUo|5-J@p?QR zKTtZlZ|$if9bZ;D(zo;^{Ezi`9%)GPo(A=mNa>8qU8Joy|EKtuU+VP6upZN@%r@eD?WV2yL-Bm4|1Kj^>8Vq% zsvI7Nj!(U)&RUc3XUXcsbK4Mgn5K=&5^&K@uQe9!Y=;M{WC zke+1Q*Mkp@&gSN<;_a;9PXoP4zvYo12Ood`>Ykz3tjXOzf%bP)?`n1Sbba~7VBgY)4Pr-KAo<9#Ea!0uVh za+7^B^*D7Xe2j9;2330hB(*Vr(0d`bn)tFGU7PD9O%>$fg#SYBAllJL@}b0JLhbJ6J@^ zQ@WsYFh)yhI+%PR=j%Nj>TFDkI;4zB($5CRNWbdv_!Zns8tZ?To;vlN6K~Vst)2Yu zw@*DueHYrvDC*a_y@gwKI;v~f+^Fkg{!%f2hs^RPHB6?lU}9F zaxZJ6k77rAf1W=3%Gz&ry)L(t=OK?}KhyJMx;yyES9tq~_ky~UOTxEpf)7V=zjgzD z{58RoBc2bsFC`tr>w|>!=uA)vpYZ&Rd6naC{6Bx)k|5q!|By5}UiLI)Kppg^sk^_9 zmG4v7kBybvbGVhNO!2n_n&NS;uPMzcn?Ha1)OWsuKM%JW{QL>OtGsUMq$T`3*77kJUqx6jOrzJ!0ri{?W+MH-e64|llrl-&P#KR(wPqdjL3v^!$7XFyx; z0jj)Q`{b_g;pKc2ly2(b`Ib|sexh>dOz;*PZLXqPvUzQ9Ln?SWk?MpO3`0Jj7VxPY zb?if1^T&RS9dXKY;r|+_sEHc29tP@Y>$%(%J7#N@t(G zllHCj)TE+5nXGe0&kgB~J(GE_OK;DOO~HS?wMj4aeBMiVqsCpd zHSg1A!oC8ZsDMv+*^T*n(pmv+#Uc9e@TV!a?{T`k*Qb5mk??-7>w_w@PxU@YAN`5- z(GlHidoHF-7jmAGFt4brufz|(N2m{eggAG91X-Q1+sn7+cU?%^BF)Lp>_ljDcSQEP z;zw%B!xG*R^$o=_IsHO@7jD9;@0A?0Am1GoIZLM0yH5YgR9L`2loaNn|UZQ`s|)MjD;h3olG1 zx@Yp%?-<{lQXF3ke8eovo*hs^g3R-a%m_0H2+2DEF%d_$JGGSV<4fh7(dt&tJU>$LN3wc2DMyPS0 zM!YL0Zp~+7{rKD1mDB#qY5(Qvxji?uy^xZF_D>(} z*+4v1*+)5BbWiR|-kBS5b8GJDYw5%3>=UX>h8mSu#@fZdynSkv`qT~dC(#mbNYi(c z9&gUU6L3Ew`y_GvPkt+I_f6&fs=_xF{zt#;`=+MhA#3lCzN=o_;vUI~KZYlqKnCd~ zt%rxhOX!Q-^O!56oRi#tp77ekcM0_l!$RmGLmUxZ@gwf`4BfeFyt0A%p&pa;p%R}y zMVk-h0QIYqZ>!B>jZ+Ri5Ts*h~6V4o3|Cig=V}myQx-x`n*icZtRS8uK|`QT+da+h>P%BDZ(; zh}Mr)e?uQFn3pjBBRsL_A9=$0oy0d`9QCHWZDAfp{=1MVy8niLBH7&d+9c$&s%JC7kHhpvYM+o@)3IjzRzNcfNB4 zz-Q0%E`8{YJLiF4T)%$(55M;x9_!#Yb8+E)1W$~)?eV2F#^O1dO)#+O_>n3_SEDDR z$DyTWpqYpB-v1%b`zn4Jnul}OrNLGC6#3G-!X9l9~^v?_FsVzr}iKLj)9=HDf;ruaG z626GmXHi0F#^>Xe47aCEzGR-FAEspfgfoCLYmVP3nbn`1l6i?UfHE&DGON!yCHE$0 z0A*fRWafibP;&of!4&x)V`d*5PWe4uLNEO6`2748E#aXY!gaLjYW9|8ySRZ?%k;rsN27-&5TNm)cqq(~jv!Nhr z`^mECOr>xocEVMLorWq$*{dxIHyCyqZZh0#xWzDQxXp08;SR%{hPw=R8$M&$ZMes9 zui-w!{e}k&4;sE^7#JQhJZyN>@R;Fo!xM%l4aMk{-%`U0!z#m+;Yh=3!x}^1aE#$N z!|{e`!%2pd4R12c7*017FH(BC3^y5WHr!&EHQZ*n-EfEDPQzV>yA7W)>^9tExYux> z;eNvdh6fE_GYkw586Gw~YIw}>xZw%IlZG(AsN4-J48whH)tR`m*o?GX&yBN()t*dD>KJ%V9-1iLMK*d8&5?GX&yBN()t z*dD>KJ%WLS58ET=uswoddj!Mw2!`zu4BI0ZwnxxY`GBQ{6^2!YDZ`P5)rK{OzTp_d zafag!(}t4_CmY^mm@%AgIKyzJVXfgD!?}j@4C@RV43`)-8@3sC7_Ky2W!P!B&Tzfq z2E#7HO@^Bdw-{y(w;66X++n!WaF^k3!)FY;4fh!CHQZ;o-|&FpLBrP!1F)tBd5`v6 z!$rVtLN<=;j8Y9B`z}KXhcat_o!r|H9c11m^f@~r98G%?Lhq#TGGs?&PWj(txX-Yr z0(-)*G~6S^Z3`km7?1FB=rc2*@FV%O2{S_c1w!Jjssf=iS4g}o&3s6RzXsNv5(Uxn*;BGiiIF|k3hT3N&b7?BV$%Y+&WH@qQ zUkYv`WoFg0T1Xv}h0qMiuz$}pbDfZlHY?4%N~l1;*8Y`Z@#N}u+o`B+Hjx;^kD`r= z1-Jg25+ji&+y>+J#)MYC8~YT~6fs>D2|iMj0F4=(qI-sFJpUy{XF?P55p~mzKh?YZ zNYmB4ThXHD_C0F9bSGw~lXFnw6Gq-8IE%EtzDoL(*T91WZbzt{6SPVWTt6khaTB?Wrl;5UPXFyUH0r7nhKtEvO3mnCeYMt-h*w zUEZNrPgW%tvbOB3jcr z^W8<;$U$r`+KC$);=Boc z_TD?ykW=)}-e$&gX)mM7*eO>t#r(RI&LF&}>5h5vCDIo1k~I8@H9L4yHV3@6Y<#58+j>KxYbsIVom?2w`t9d2>i_C5PBxpwZ4DT2Ny)1qehvRPp@eYk;~6o-4E z<&^WsKIlm|_9Tc+h+8 zO>2iGy0g7idp6{%_M-i1K?!dkZ@7GTpm*|0xurvI^?9iUJqbS<48Nu|cd5Ui=RYJm zgMsc?u5y_LH6}aP`aS$Hrd-R4tOj}u0(mY}%1fc&H*z;o$BG4gG6&PpD0p#r7WBRK zeQ2G%uKydm({P}^uQGJXmHh8x;rfN#y3oFUm~#HKP7?3?nx7h|?|&SB@xH$k^I-aZ zfWIvIOntwTct3zYr(D{L`@ZJK?s1+#=dVw0Xw)_9XFSJs-Me^uE{NhQ^!*A(w*P;= z!5GUPWv-ffH3G}H_e^PV%GI$jV#AS3 zEg-G$p!-g#?W(QCWzsBWM}wb*-^Fd~Te#gM52jq0zP9zb_QABR3GeakX)moi{AcSt zMPeLl?Ouw##Qtlqm-xvZjd^|ckMLe<(n~+f9e*X`0q`n(Uv%VGa`&{DRjtp++ z+P6bec%QO&<_i{%Q$-d7jb&BmQW>@o%INw-Dyv$b>luLUt-_V-O zeFa~Ac|;z)KHVpdp7EPBVZtQye&+lBH${xF@J(`aY?@!wJlm>T_y*(GWL;+fZ#+`M z92&AUGySuBnFHhcgZpyinKA7@gl|HZcK_`4QXf9kT?LO}PbOyHUzaSHnJ;@29s}>; zP3u_!G`J$+z8qs$Xl6z8a1uWbPsfY=SUIV_ZtXjt{O>ff{n3l(9)Bv9Z6&*Q74qR) zZDcVJe{$!Om0$6v(S@*4yVIDrWnumDM&59YGbv}+$El)t&nmAfL~`~R)^AFVNK>xr zqj7$vGIz?=R58EO-82L7t6K@^`n+iDMNiTXQ;yzL%yE9@L(lP(IKP^Wzc{}-iFq*m zYCMVP#_yTqiTnC(!wWU)?a_-SDgT_||~tzm840>Yqj^Ys`%$ z>fk?}@T@kSS~qJh%=;93(!I(GGNShS0dLA;YTAwB8We&gI@%T&Wb!s5_$E_`7@gzRtJI|onnaMvL z^A|2z8jZKYz477(vSOn;|K~5_frjY2d-mj>{_&hW&U0?bJz15pDaTX9XX0{-*3-m8 zoJ`?($_(sW{}qV?=yCdC%F&ySIZjXeupJM1C%Na{jJd?4&LsC}t@KKlIMAiNSnko7 zuyFu?F}a7qf9o}8TC8x^&d5Tf+ zF1;Q5>VlfXZ2i8m?7TC+-UPtD4}wm$@|9a!7XNnJ>e? z?#q=jS2Q%UM{`7#As^%|+A7|`hVQU1f^(|goDuI+5eLN2d$Wnm&+|jm0rJ*7?w(j1 zQ1+y!O=&BEr;)6A@I!vX7d6&{YxN$pc_+;`m3lJ+nNKH6rDNrPl;5`w9l5UTq0)y- z9%0m%YzU{He*EI}xa~Q|3kQ<14i)m{dPijOPBNDA-zQ@g&pRz^69s)A&Vq~uv9rhN z&LtU3W$2VE`C=K%harepIpzEn%UE^Bzr+*y#-CQkYJlEgWUNj26aPAsj5VEj2P0$Y ze8505R*ZiQa=+H#-={2=vBbX;Y4I%XAxf8U{xthUqC30RH;XHV_-Ro@e9*F&J@*b6;Q?BHTdCze?NbYjV`77o<+KZXO z)0udWc-dfhPwgOik2gr(GyXKZhw)y@yF6}maoq2std=XWUowUBDaUDX(qwoyP z2|b!juMTvd0sNXGy)#FUQOUICuDL%2JDr73!Hdo`_PPnr^P(44&bdDS>yEvYr-8;^ zaamq%&h`0U8H=?&5RSzhONfuj(geGhikJ-QuH8^h`E~b(7S`=;Y-aJEJ+5(Xjm=aq zPC0%c9uXf)X+2wYbEdIb8-C*T>F7-_qPH(b@14eGn+Z1<{<0T;-DsLXA&oA*3=Rrv zJ@HN~m`*=7(-`iZ$7a0JJeaZBd))U^l}5ghoPzA&c~9tWjU!5a6*@j|BePSIAI`P? zJOhnU)JG%bT1jLYX#U2{11oRhA#wRs^*kmD8vd6#c8c{1^=1*n+?PXz>kc#p~$D`9?GGrg3x1rM-Ag zM|^UiGc3p@ZjvUHPR=~1GvV4uj5)JhvT^{qMB^PVGmHCenMX-wOK94ar2+TO!b?cv zk)fK8R+hV8&L`92EzF~EKa}n;E6Z?pCABWR$H}X!?V(I6%QSW&tmGEWC6}gu^m@4$ z?d>VcObz|(?rFMCKfV-iBP(&6dJd0e-sdVeMArf%%Yk^3+KH6%C7z_d>6F~YIs+K` zHFn^h5YXXe$BLHmIC<+ro>E)ZxJ;*8rV~bU%yWf~FTHnprZ2{`$C7?y``*)(jryVZ zfYccS~kgT*|_tz8xKq+19mdQR=&hr&AL=uN>Q&XY>%f2v>E#d*>!%yFJ{5c6Po zQU!_V#;^F1OQWM7=Sh3<7f+u{dofQMN%|H*%PHrtZ=Qy`eFh=R+%VCmnFnz;=2#R- zVXPF*Gc&K!-I;=a3?oUS$RGS-Ht&~^)fDG^CFf}Fg*jsJA-F|&?uD18LrlPLG^bld zMouUD%)sqjmwjA42nU)I(Hgmw@=}-+Q5Cs!w=KGh={=5d3hUjXb@rISZ%RH#Z zV=(96G`IWCbFr}gLGy*6W6+nw`v8$YN|7Cs^j+QEEcu}{ylZhzxQ_-t z@Tix1`E~Vs_`xXoEyHgpXJl%7DC^2He|jK0#z2KTa4EJ*&-U}_uE8|(vBi9$22W0@ zo~OD4vbPSc?igD2TgE3`%Z_qXLQ8afLU|LPh_@59)k`9;;eVOO7Ma5_lA1+FFJ*Rf^Lvc9e=oOEX#z0SXt~k_k9KGUkQh?iF_*IHT?8on!WRd`X z%9~Rz?Zq<5B+@s~IH@3$v@K{~ItYGs!}WugNwlXM)n6v**14*ATHj`TbQR|#>^>3r z*HGjXc>5^ofjv2Tb7FR{_)!UK(b|*a-id*em*}q3ME9|fmd-zL2LQ6o!19Xf*{KG8 z%+Q^si-vkH^80s1c2T~Z>=IU`<1sSe_MZ5LlU+huzx_Nz{YCx6ad?emRDMpW?@ATR zE}OB7_ciCQSaxx9CTEge_CRkiyx=(gvS{%ZX~pY!O?-SJnI!&AIvp?2oXJ443v(vd zznit3>n9BU4YPewCoJ#+y{mJVIU2D?9xj zyX=~=JaqK{Md^)o4_xW@j2p*((&3F`XUjc&yQ=%8{21N04DYJqPU~w(OYI+Q&DZ`F z`YH5A=8Z{5vW|7>Ea_>>>aO%7WAXnX{8n+-wCBIV-kVvuKY_<-%%wMB65Y`|NTLhR z#^29gukc3n9%FBf3zPI^H@uhqg_SOxXgPcCFc=l5qcdVd(nGlKeRl7+pgEG?Bl%qe zzQ#Ky`u&%R%X^AEZR|Pr72TNq5xPP9xrt z{@3!|g#YZ-YkJOCdgz1qB)wNR-^QKqpWd3!KCm^v{-3ty^$y6rH|uQW@LtY#GRL}B zbFri961;sy9CgW0SxdP=|>Vxww^H_e+k+DGT! zcqmD&4yKG=5UfG#J(ubG&JM zv<*Oa1#~M^pYROoad<`g;S2WgPDDay$=ExHxvZP8X@w==UKb1hH>V3v{Y7ctNS~O+Ud@g9tDvJcIsV7}+vHy)yjMrQ-oG7wqhC9m zYwhs9LOcBCAlhLCX{e^Jsm;+J1HI3reOT%zo}c|5ZSed2{=P?hOR0WoV{^&hfNgAm z{QN)aL;0EaqyG8%BKevAKhA!B%Ha*)9PwQ6{H1(r$mqwkW}`S z3i4C!!C%Vt2N*cX)#_oK}mRv)ZJQx3n9|JCGQJo=m0+IwB%Pn4C)OYP(@ zD35S%sI4pNo0OIKlz5bv?hN~nc$WN(w6>vke$3+?I^KlS8*=wkP9Y86o>jT?#>ObU zE4Lp%33wZAF|h=@@)U0)75LNLxrNw;bg4(LtuucqGL_oqcX{i?@q_cI??2|Po-?&q zrRfgRkw|BHLjFKq>${|PZw9Py;&i-s3gH}2QT&e)rh|8M6mH9Dc-JJt3<-JV#{5I4 ziz8NE<1B6Je`<$H+vf%mjsgnv6!LKk*L1pYu{>1~?iToQgV*_TDRGW8zIG2`H|y;h z^0wLXruR_>ETdTZPf!P+{b+{o_Ozamo~7kh=+%X9;q?FZe3iFe-oR}Wby$S693g7g}=05w;X}^-D(a_n? z+sH1yuh7Q#V0JW%)9@@b)!wwv?2CNY|4Qi~Zly(G2Krv_B21~3X&L^L-bFn=vP4br zwfrTV8RDD!vl`wN6W^4)A^yP|MLm*tsuH~2%$+kQQ@jC}wD+66>_o(~8;@hRAj$hW zUiNan?GsbaZq5H2(veK*{Un_e*m5Z8cLquJ{*UEdqy+E4RUsQ`edm1qq*4!4P8aq5 z^5m&eSsxh;{#e7e!F%@c*Oiv4)D(DVUwW$WuQWAhIF}i0%`YKcn|hQc&Igd52FweR z^TO|vzVEM}JoS?>PQo4H9i#;H`ESX;WUC(--a~sz@UG^6qufhKyW|KVJiGTXxsegQ znWOh|^nNi$Pj4CP9pmpJA4_dRYo1SYa~fLu=sL6%qIRe~_VUm^j)a!{(>)^p27M2D zCHm{=@1qZ({}-C~;k-2ZcC_A57M=LI7xh(8ihF9&nRU%0IJNtIav!tqyoV|t9`Z=} z`m!&UK3ei6&NxgTIwSd+@G%{pzKI^yQ9MrR_muJc`z?4kM`e%{;7w(r) zTS|oYB=@f^hI8gt>()-mJw7)h z^Frqc%Dk+|98O}Go!nO!xp$daDc3tT-zakbk7h0fhpE-GG|<-fj?J?Aj)hCKccQ6f zarlVM^eLa7J%8#gx85=9mieE&pi@CxCms7)ndUL$E+!fL}B zL*HWFGwd*2 zX}HR;({P>PdczHdU51+sHyds-%o=Vp+-|tTaHrue!`+6@7c--)W;YmZ7PgqXc52EtYc}Ahuc7!^QDAaxoq1GdW+G{WL4YhV7 z^EgB8b(dLpvhU*PC7y9{?5K4aKzxW{m>;XcFth6fA}8op*2 z7#=b_YcMdoJV@8QqPdks$r2|t-nlCXlXLVYYk zAh%xY=-vVOB_9wi#|W`yFQ9X}C)WJ&Mh{nE3Ur zOkQqlL>z>dJe8!ZB+n!(zzTD19}svS&+v^{eEZ`h6zO-tyHswGVF<)!~%tRcbt%rs-wSQtPO5$p_N%Bdxlh zhK8jUxbDHrb_U=fdZ6Y=`AC&f#%FU0#FuITx_rpq<>MiKiyrJER9Wby?YF?u%kn$!Z$JK=TioAYnm^e|9l)Q) z4Nkc*8bid>w}XzfMKFzRk~&hs5U!9KGT_MUt}}J@H6K z?+$*)`N$5;gW)6M*E`WvdB{6N&(V+bkqyL)7*tR$?ZtdVa{fE+DO%pBBU@*@bM=P8 zNsTk}5se*C{rCuL7~0#|i-Z);_Z0I4nVnL8&$am#)kmLlts$}*sQsfJ9@=;EjJTL@rqu&-`|co-tQYQ52oL5!rxBxnfm>7;?*3fQ!eer{eBe= z_c>3X^T+<78z%aF>P%;@uDyQn@0o2<0mu6N`)I{8>RT3b*w4FjMN@|yZ)R#e6sKNtH)3Cg;-EU}I)YL+RQGEUc{M|Kx{k4j- z_AXyzFu%EJSyKm*FKldVfY7Z?&5eZr{I56==I*BUjuq6A!nG5mt*Jdse{Fl~qNZjQ ze1Df&jmukCv@e8ID^F-sZ!H}Z{fajIzN=+vOY7%aTvErX91^VW>Ae3XtTSrA(!H#S zbYZVH+ksZ!Qh%L?o`ar=R=d9mJqbM?JqBHa9*M3(m!eNX<2d>#`Ve{@cKV(kK<`8E zK|h1uh2DYQhTej%n!-9TGHVK%wUpn#(0T;Oo~@`FM!*?pMqKNufZ+gkHO9055Y~~^I#Y2F5x#h^F)3< z>s3K!9s6jawYvX|JH_HHq5NvmhtL`HKJ+fM<|sSSX|(D<=F=k4gt z=yhm~%ha#upl6_OLXSbG&|)3OMMsK3MimerGmH!!qJLv>pMH+s1L3~=QA$f=sn&O) zEX-@4Wm!1(?8{r^uD%ed$bFkD7sAJt)@kBO>n!Zq4wcGiIR4`@qRRP$<}Q#qltV(c zB~hfo8<B<1Kej-Oq1Y*0!R(aef=;5sJnlB%ua7jZJ(*)v3(m496R$4JR2+HdH&1 z|BT^uL$wjv&orzxRQr+rT*G;Wb%qUwOAMP0+YCDl)gDD>m0_phI>Ysb8w|Hl4>gSQ zRUevr3+Lj&9cJEXs6Hb1a4sHv#?0M@dkn+5c-+;mu-dRjNMrGxyUL@=#-fzpyBmu}Q-KnH zqh`miUztmcoPHPN)$tS$#b21WQ`;X-IXYF!54qoFC|Oc!27c6sosvJPThNj@GiWIu zsGu?;xsj?Zj6XHMC%R5GLqPq}DM#!^-;%Ixg34!eKCS?H>H4 zNQmZ7G$$f@G^H@k_;~gJiI=?MluLWDTw6_IKJPq%&R=0(Yq)P8dA z2VfQS_CT`7V9v&f*V+BE3%H2!#ow-RvRY`L()Y`KFO@HMHbzxg7#Fv-ytlb8w-N!- z-z9$H_=Nb0%FZeEFDZ>zUEh$o3oTycl=F837V-X{C6M~3`h1+9d>nJU|38CyF#Z21 z{<3JrA$_|3zkzs1;_j47dvX8QH&%OtoO1s9`hR$&_l)yd6Vo>g`Yw?}N_edQ52e-W zyb1dvd#C)vCwoem2QSsRlbxLLZHvyvaQ|WNN$z|tfhUx}3v^D`>k4`)(% z=Ycubul@9QvFF=+@;q;Sko5MyUebN=b!s)oorpI!ikAf0-l5P)baDQUy`L@&yyHk>`@s`k;dJgzF30+MasW z9~6JWzpbYveM?YT$DT#@VOEfi($0>rRjsOPq}(yPsxNefj4PHo{@Gha&V=` zUPxLG?-0x$#=hh0O6ndCyew~?r#}{y!aV*v8$xa&P!)UvmbFnDe)jeUQx=P#6)UZPq|P2 zQ|txwi8uUK=sZJs|HBmt?`6(U>fO3|Io4J9rZn}G*R2Wm8%^zZq&%K1O|hrg|6)kv zJlcooR6;}Tfp%4y@Y*@UbU*E68uh1rfNOgwOp(fHTGtIXaDlR{2xWLOZUoe%unWb-}$+h@|o~#+MV0w1_4K>8_cng(ynk3Gb>y?AGkmuvG<$dF;P;Qd?cLqWnC8*)woH)m zDgV^NJ@0d2a2wLLE>L+V{9Ag;yVnG#yz(BM;mrgadv4~Q*ktM!`r((u_NX&5nbfqf zFY1g@dHTTX89(w@0?*2Lv-4htcRqM?i0{G`A5oc^c_MpG%e(OpPq>+P(yF@G2HLM% zMxBlxsu7NbII{*a|^033r3I3=1$c-OZ{YX3&xBN zN+x|dq8}U&!-zMCzRvTMK%cpn%Kt$}6_u&t^ zzc#FojODYCp8ff%z4WJj%GZ6pC6hMfXg`%nzc21&>1^FeHG=O1#mRdGA>P@W9FtX_ z2-A}!J>MhTb82IhWjT3J*_BbYF5W-JjrQDc?j(cRsd0ROPqXKBSH<+9yDO6aC*1Iw z{C5#N9BqFM_n9~v9ZAvX_ZHNxL+2*HTsgFQ>iKoy|KzP9ocrrIPX1K6475+S(q%eC zCv`=Zx^E(`O8CT#j#)^K8dqdan^|Qz+$;C4FLKxVqEiZgi!*>ShqE*8dWw$Ewa))s z{R8uxTU!>-Z((AWIb+?G#ZBUjs>2&`G|rALu(10pmN$0DQOnG23Fb3sEMj;y6hs3p zPgqMdn&+q!VqPaC@@n(1`3Tu-zM%#IZ3%hNUMb0M+JjUrEVKC;%#u&F=jq+e6Erqo z9m?(lWinjIDoFNc^O6jHK{NS7rT7{?hmny?DH8 zlTNvxUW- zGmO)fF-C6(k511z1Bx@xCyW^rMp&3n zs9~3y_Sq!skl*=sBx;SH^UY43KjkQUX)4AsUqAN7l3;0iVUNB+*c~jnk#E@rYcX>U z|H9Qh7vXov`TkqD9X>@lwJh?yrk19T2Z*jdTpf!tshQ<` z`o4I@J8A9nEY|Pj|J}HW?qL0g=fX+rPBl=*XyZ~F^J((J?-Aou8!z}s!mCUpr=Q39 zoHvrl$&^ER8{b~a<~+)({Mmlr`84=kagyHlnrlFS1|rbw@rCj;nQ_9 zpA(Hl?L$4tJWuLf#+1J??;|Jb+nox>Cbhj!5TB2H>8GFX<$Qnd9hkjNjZw5dwK3oI z`5|wmQX0qH)mylgcyphJjj8)$f=Y!`Plhra6aRVWY-si^Sags=clvZV$Akk8{>RS{jOx^ zC%Q|zZtE_|e!M$P7}<3~lM^AmkE`s=ZgXh&>E1sN@g9x)UzPl;v(UnKCX#|8Vg+8&ETf45^%K0(UO1U5YPh0cblUwuWXA^}m=dl3UFH$S-b%^w?W*yW=yK6vu2t{X3UOYHjt*FCK_k-u`{O!=ERH@Chg z$s9#;w8q5`_mtA+m8K<}XKhF^uJ<La#?#5!*Kz;iv zTgoEsKlSa&{!`yiX4hs*x~A@2LVk3fS9c3c=IkQBdnR{1_3d=_sqgDd?MM6tJ)hz% zylaDMmui#uO1`JhSJX1*u0yxMV*~XW@q}5uUrTtS!oHDyD3?q>k>eZAT+6-Tcc3)u?N3<3G^@i2jxR$8L3PJ z!OamKyZNivWjCEV^*4t9hBn#mGzv_P^lN|ekzK@h=&I__RzjkAOURL$;XwN*#I+1=gegL=tJgtKM`3ugm`=c*~C-U7Q9paVn#|u1v!Eo#@cnSVT9p#n(B;O~|Or~FXUFG2N zvRLzogiG)Z6CHTZD5W=%CXH#O@uhIjL!8&k|C+Z?{p3rWQ6?_l-ivtu_FuLS2?`F31f zwe+cr@iU@=avAdA2QR_j#n1UWbKx8zeRF@w3&X@W_{JgH%?kO|n9D8=`Q7L%lEG5a zCVTkStBU&%xl?2ks5pFo>dTyEC%)j7)MbhN*MBhkd=2?m+AWh_24UG+qt zs>9s;_NiCpetw*u@uEi3G;`-udAh(6@kDq|Kb}`-?sXn>dJ``V`F;&Ft1WLXKjHx% z;gtr(e>(m;!_p}~abEj2c~%(B(>faBxl@T>eSdq&rFrqpl5{2*Hj(dbYGYpC+RF)d z5ZK2^Wu-;Jd*ml z*Vg-8`fTkk$}1c6p7_=ax%tEcKj@XLt9~Jw>%+(|A@B7c&pksvHCMA0Y@*M@o+{x# zKe>(HpWyfHAm{Q&Wl0AzVRUymd47U^5cWy>{y6*~KU8*?UK+N6W28xKsReu8FSVZE z;zQ#ow?qowOrFKFRW_@kljzFkW^ra)-w^rH9;H*0p6z}AN1msR&+gsH+|f_%+fCcO zZ_IU<-tzG`cHU0DrqO=y;~PBh{}KMWKVQ>RvvXs<#`3KEYM$wn@8>QT`cCb2@7#_a zB%Hp5&6M*r?glDhJ~;8g=X#gWc1mtsldC4(YJ(Nt1>BwR)CI{2Ye!(dKz&W^a4F%R zxuAbJ-Dc&qAA5Z}Rfgh6YKOsHq$iW@Ju&5loTH)kM7fqR7oEgk+1=2ToGm$Bd|Y`_ zSjms-gOa@^!*Qp}DAh+Y#vjGQk&7;%kBv&uz7wf>c-5Mqn9nm;Sm5)+-aZxbdC9D4 z(wpeinh7#m9pP&^*RM1X&yf2c2_9)%8;A#en>1cRyH&cRt8qWI>!-c%|MQ>rmUMkF zcYgXCIkn}Fz46hvl$IB)E_eU()IG|#_<;Jk%J6*ZYoEC@XaDhkiOFp{etGIu)^Wmi zqOzcDkBp-n;59uJwXB_}ePh4p@~!y>>}Y4mLlIvXQkUw{Sr3o;C}&K{*gcdtBr9+~ zL}ia;j;=`N$kCQ`$AG>cUMF+u-4FGAn)Y3$JD7qD-^06lhdj%8Dg98-hPLcWN!pgi zP#U{zsLQ@IRP(OHr8!T<$vnLJ<*O;emDfMK|KYkd`&DkABK;S7_xFDK?NdKceWoUI zhm$`eSi_wX-h)rPsc(?JJ*9km4v?l4{AKE8+1xJ`ALVwaXKP;L-JvPspzO5v?!Gw{ zd~=>top)zPB={!kyQA+9{`eNn>J8Jz_nNU_j`7tneS5@T9!`8YxbB0x+X}wK_{V#& z>&-31v+6mnfo|&j|T1XTOnVfEG8j8&maxzV*~N|Cny7bhx9EhgEw85 z%Jxd`Rhx6)A^lF&-P%(|9hWnfA3=MWi%``8Q)@uaLCZWH6u$}MBEIv~uDG`^c|Ux! z{)Jo_=53FWuTJ{sYIr;5CA8~m{5FFMcak*En-Gl?SGzi+jMR>okk-uTt@-ye*Rgik znrIA3ed;^17TL&K`uLk=VR=UW=iy)D5A}ta`kuf;tDznC+w?2Bwb*5dSNxDNuT)(n zd9$4URH<*PE}{=B z?dppq>Bn-HafcQ%L)iz(r^-rsuL&aA0=H0}kpHVxFN6`_ZHB&h+!8;kmooa%O1>W| zvoB0q3r}AgmboiS$M3?!`OlTW$5@*fRfvN!RUChaU2?*+y_YkWrTHw;{uVOs$n@94a)&>RO7UA`&2Pd7 zB!5t^g>jhVDzDAP)0Efa_<4!;b`xb*O`WQqH^QUWVdv+lzdFYL`YrcVRJWIsrwZCj zlKg#!_?ZKKROyN4+JfE`vWw(|Wu)~`dTUsKul-Wmj|_h;(23sZ-V}bH!|&aL(Okw*wx0cX>HmG}1E1OY=Kr|o*ju;W zb3ByG%i-fcesJeo!|vVs<}K#eOKp+> zU6n)LTKt)xza?2Im5I(M(yv(#mip%}oI3Rd#*A((8Kt*N=_OAa$V-&3)2FriG-*wp zrL>00jHT11mCoyCzTiexDR*RsiIbiJEX1GS=+{TYQ%^Ks>p$%oV zA1J%Cx@^`(WwS3Vn{z?gT_elx9#QtG;bn7&m3=x@cF+6E?j2e-?|o%|P*rx{`DODf zXAC`u%Ll@|8esqVDaIUKAl_+Z8(rgWxF?tO|L7gO51*Ps57(0^UQPvow7_((~i=@r{>k5vs^Nlr_vO_k&u%3034ivXID#el=~+xw~IN?TzoB-t)rrYE3Fq z%n-zs?jY*#|NPMZn1UZI81yOKG1TAx`$GSH?T-c1n9?0Y{rxk8g8SxzAAO(uR0$`` z`}-dj`tK@uDHKd-!yT=md%+nqGEAscM2W&lfyl0e)!RZ*ny*VDr*K_25&ky|pag3D z{a?U_oJgskB?_@cb|w98y;k_@es2z{+D#Y<1IT=96trNTWoGdg-N`hs$bDgvd1;aP z{vz||i_B|^%wH@rKUQS!tE=c+ku7rnqayR46q&zQWd321`Q;+>e=jm0E;652=xb4W zjupB8zan#Kk^lD55pJrx1bbp`saWjjz4yXOk zF7iLu%-chE+B5qUwEPqkp{3%_reB|jFnwmq<*sY4mUf_%zTNM}LQ;)qmKIGhzG zSaa)wdM@g+7-NTZB2Q64E+X4dT4biEjnt$W%2D1mlRU2uOv2V043`)-8@3sC7_KyA zU=fDvG+bx6-f)9qm*FNu^&iEz#V~8Q%~0(??rJZ>orb#%cN;!q*loDSaIfJ$!~KQ_ z3=bNN!XL!)?knkqtn=+7%f#Za1EXoKO9d75F zo6K-7NjL>M!Ylc<3i%AaW_ZZ(uweskHB;FyA)FT7Nv?G4l9_ZLGf_yy0ZS z>4tNKgbRe^Yb9k?GoA7f-hxD46`8vYPYNk7-K|tJ0e`}4kr|NYYHlUJDez;+d_v-v zETMWx8%{EuYZxNy z6y6^h`J)=Vizi0=UXq`nJj^@>Onx%jFD3VCGuH_5C#=DKm9RG2yMHmb&HQZ_Qq~G5 ze=E)322lRC$Q}AwA@ss<_$OIj@@k8ZwvaaSBq8Or+sw}hS)kZ!=6%8sL}xn{{;